| # Realm Builder | |
| The Realm Builder library exists to facilitate integration testing of | |
| components by allowing for the run-time construction of [realms][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. For a comprehensive | |
| list of features and supported languages, see the | |
| [Language feature matrix](#language-feature-matrix). | |
| ## Add Dependencies {#add-deps} | |
| The Realm Builder client libraries rely on special capabilities to work. Therefore, | |
| tests using this library must include the necessary shard in their | |
| test component's manifest: | |
| ```json5 | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/rust/meta/sample_realm.cml" region_tag="include_shard_rust" adjust_indentation="auto" %} | |
| ``` | |
| Afterwards, you should add the GN dependency for your test's language: | |
| * {Rust} | |
| **Add the Rust Realm Builder library to your `BUILD.gn` file** | |
| ```gn | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/rust/BUILD.gn" region_tag="realm_builder_dep_rust" adjust_indentation="auto" %} | |
| ``` | |
| * {C++} | |
| **Add the C++ Realm Builder library to your `BUILD.gn` file** | |
| ```gn | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/cpp/BUILD.gn" region_tag="realm_builder_dep_cpp" adjust_indentation="auto" %} | |
| ``` | |
| ## Initialize Realm Builder {#init-realm} | |
| After adding the necessary dependencies, initialize Realm Builder inside your | |
| test component. | |
| <span class="compare-better">Recommended</span>: Initialize a separate builder instance | |
| for each test case. | |
| <span class="compare-worse">Not recommended</span>: Use a shared builder instance between | |
| all test cases. | |
| * {Rust} | |
| This section assumes that you are writing an asynchronous test and that | |
| some part of your component looks similar to this: | |
| ```rust | |
| #[fuchsia::test] | |
| async fn test() -> Result<(), Error> { | |
| // ... | |
| } | |
| ``` | |
| **Import Realm Builder library** | |
| ```rust | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/rust/src/lib.rs" region_tag="import_statement_rust" adjust_indentation="auto" %} | |
| ``` | |
| **Initialize `RealmBuilder` struct** | |
| 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. | |
| ```rust | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/rust/src/lib.rs" region_tag="init_realm_builder_rust" adjust_indentation="auto" %} | |
| ``` | |
| * {C++} | |
| This section assumes that you are writing an asynchronous test and that | |
| your testing is executing inside a message loop. Typically, such cases look | |
| like this: | |
| ```cpp | |
| #include <lib/async-loop/cpp/loop.h> | |
| #include <lib/async-loop/default.h> | |
| TEST(SampleTest, CallEcho) { | |
| async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread); | |
| // Test code below | |
| } | |
| ``` | |
| **Import Realm Builder library** | |
| ```cpp | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/cpp/sample.cc" region_tag="import_statement_cpp" adjust_indentation="auto" %} | |
| ``` | |
| **Use library namespace** | |
| This step is optional. It imports the entire library's namespace, | |
| for convenience when writing and reading tests. | |
| ```cpp | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/cpp/sample.cc" region_tag="use_namespace_cpp" adjust_indentation="auto" %} | |
| ``` | |
| **Initialize `Realm::Builder` class** | |
| Create a new `Realm::Builder` 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. | |
| ```cpp | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/cpp/sample.cc" region_tag="init_realm_builder_cpp" adjust_indentation="auto" %} | |
| ``` | |
| ## Construct Realm {#construct-realm} | |
| With the constructed Realm Builder object for your target, you can now begin | |
| assembling the realm. | |
| Use the Realm Builder instance to add child components to the realm with the | |
| language's 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`][children] section of the component manifest. | |
| 1. **Component source:** Defines how the component is created when the realm is | |
| built. For static components, this should be a `URL` with a | |
| valid [component URL][component-urls]. This maps to the `url` attribute of | |
| an instance listed in the [`children`][children] section of a component | |
| manifest. | |
| The example below adds two static child components to the created realm: | |
| * Component `echo_server` loads from an absolute component URL | |
| * Component `echo_client` loads from a relative component URL | |
| * {Rust} | |
| ```rust | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/rust/src/lib.rs" region_tag="add_component_rust" adjust_indentation="auto" %} | |
| ``` | |
| * {C++} | |
| ```cpp | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/cpp/sample.cc" region_tag="add_component_cpp" adjust_indentation="auto" %} | |
| ``` | |
| Note: Realm Builder interprets component sources defined using a | |
| [fragment-only URL][fragment-only-url] to be contained in the same package as | |
| the test component (for example, `#meta/other-component.cm`). | |
| ### Adding Mock Components {#mock-components} | |
| Mock components allow tests to supply a local implementation that behaves as a | |
| dedicated component. Realm Builder implements the protocols that enables the | |
| component framework to treat the local implementation as a component and handle | |
| incoming FIDL connections. The local implementation 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 demonstrates a mock component that implements the | |
| `fidl.examples.routing.echo.Echo` protocol. | |
| First, you must implement your mock component. | |
| * {Rust} | |
| In Rust, an implementation for a mock component can look like: | |
| ```rust | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/rust/src/lib.rs" region_tag="mock_component_impl_rust" adjust_indentation="auto" %} | |
| ``` | |
| * {C++} | |
| In C++, a mock component is implemented by creating a class that inherits from | |
| LocalComponent interface and overrides the `Start` method. | |
| ```cpp | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="sdk/lib/sys/component/cpp/testing/realm_builder_types.h" region_tag="mock_interface_cpp" adjust_indentation="auto" %} | |
| ``` | |
| `LocalComponentHandles` is a class containing handles to the component's incoming | |
| and outgoing capabilities: | |
| ```cpp | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="sdk/lib/sys/component/cpp/testing/realm_builder_types.h" region_tag="mock_handles_cpp" adjust_indentation="auto" %} | |
| ``` | |
| An implementation for a mock component would look like: | |
| ```cpp | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/cpp/sample.cc" region_tag="mock_component_impl_cpp" adjust_indentation="auto" %} | |
| ``` | |
| After your mock implementation is complete, you may add it your realm: | |
| * {Rust} | |
| ```rust | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/rust/src/lib.rs" region_tag="add_mock_component_rust" adjust_indentation="auto" %} | |
| ``` | |
| * {C++} | |
| ```cpp | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/cpp/sample.cc" region_tag="add_mock_component_cpp" adjust_indentation="auto" %} | |
| ``` | |
| ## Route Capabilities {#routing} | |
| By default there are no [capability routes][cap-routes] in the created realm. | |
| To route capabilities to components using Realm Builder, call the add route | |
| function with the appropriate capability route. | |
| ### Routing between child components {#routing-between-children} | |
| The following example adds a capability route to [offer][offer] component | |
| `echo_client` the `fidl.examples.routing.echo.Echo` protocol from component | |
| `echo_server`. | |
| * {Rust} | |
| ```rust | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/rust/src/lib.rs" region_tag="route_between_children_rust" adjust_indentation="auto" %} | |
| ``` | |
| * {C++} | |
| ```cpp | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/cpp/sample.cc" region_tag="route_between_children_cpp" adjust_indentation="auto" %} | |
| ``` | |
| ### Exposing realm capabilities {#routing-from-realm} | |
| To route capabilities provided from inside the created realm to the test component, | |
| set the target of the capability route to `parent`. | |
| The created realm automatically [`exposes`][expose] the capability to its | |
| parent. This allows the Realm Builder instance to access the exposed capability. | |
| The following example exposes a `fidl.examples.routing.echo.Echo` protocol to | |
| the parent test component: | |
| * {Rust} | |
| ```rust | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/rust/src/lib.rs" region_tag="route_to_test_rust" adjust_indentation="auto" %} | |
| ``` | |
| * {C++} | |
| ```cpp | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/cpp/sample.cc" region_tag="route_to_test_cpp" adjust_indentation="auto" %} | |
| ``` | |
| ### Offering test capabilities {#routing-from-test} | |
| To route capabilities from the test component to components inside the created | |
| realm, set the source of the capability route to `parent`. This includes the | |
| capabilities provided to tests by the [Realm Builder shard][realm-builder-shard]: | |
| ```json5 | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="sdk/lib/sys/component/realm_builder_base.shard.cml" region_tag="collection_offers" adjust_indentation="auto" %} | |
| ``` | |
| Consider the following example to route the `fuchsia.logger.LogSink` protocol | |
| from the test component to the child components of the realm: | |
| * {Rust} | |
| ```rust | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/rust/src/lib.rs" region_tag="route_from_test_rust" adjust_indentation="auto" %} | |
| ``` | |
| * {C++} | |
| ```cpp | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/cpp/sample.cc" region_tag="route_from_test_cpp" adjust_indentation="auto" %} | |
| ``` | |
| ## Creating the realm {#create-realm} | |
| After you have added all the components and routes needed for the test case, | |
| use build method to create the realm and make its components ready to | |
| execute. | |
| * {Rust} | |
| ```rust | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/rust/src/lib.rs" region_tag="build_realm_rust" adjust_indentation="auto" %} | |
| ``` | |
| * {C++} | |
| ```cpp | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/cpp/sample.cc" region_tag="build_realm_cpp" adjust_indentation="auto" %} | |
| ``` | |
| Note: The constructed realm instance is immutable. You cannot change components | |
| or routes after calling the build method. | |
| Use the realm returned by the build method to perform additional tasks. | |
| Any eager components in the realm execute immediately, and any capabilities | |
| routed using `parent` are now accessible by the test. | |
| * {Rust} | |
| ```rust | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/rust/src/lib.rs" region_tag="call_echo_rust" adjust_indentation="auto" %} | |
| ``` | |
| <span class="compare-better">Recommended</span>: Call `destroy()` on the realm instance | |
| at the end of each test to ensure clean teardown before the next test case. | |
| <span class="compare-worse">Not recommended</span>: Wait for the realm object to go out | |
| of scope to signal Component Manager to destroy the realm and its children. | |
| * {C++} | |
| ```cpp | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/cpp/sample.cc" region_tag="call_echo_cpp" adjust_indentation="auto" %} | |
| ``` | |
| When the realm object goes out of scope, Component Manager destroys the | |
| realm and its children. | |
| ## Advanced Configuration {#advanced} | |
| ### Modifying generated manifests {#modifying-manifests} | |
| For cases where the capability routing features supported by the add route | |
| method 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 component. | |
| After [constructing the realm](#construct-realm): | |
| 1. Use the get decl method of the constructed realm to obtain a specific | |
| child's manifest. | |
| 1. Modify the appropriate manifest attributes. | |
| 1. Substitute the updated manifest for the component by calling the | |
| replace decl method. | |
| * {Rust} | |
| ```rust | |
| let mut root_decl = builder.get_realm_decl().await?; | |
| // root_decl is mutated in whatever way is needed | |
| builder.replace_realm_decl(root_decl).await?; | |
| let mut a_decl = builder.get_component_decl("a").await?; | |
| // a_decl is mutated in whatever way is needed | |
| builder.replace_component_decl("a", a_decl).await?; | |
| ``` | |
| * {C++} | |
| ```cpp | |
| auto root_decl = realm_builder.GetRealmDecl(); | |
| // ... root_decl is mutated as needed | |
| realm_builder.ReplaceRealmDecl(std::move(root_decl)); | |
| auto a_decl = realm_builder.GetComponentDecl("a"); | |
| // ... a_decl is mutated as needed | |
| realm_builder.ReplaceComponentDecl(std::move(a_decl)); | |
| ``` | |
| When [adding routes](#routing) 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](#create-realm). | |
| ### Test component monikers {#test-component-moniker} | |
| The moniker for a Realm Builder child component looks like the following: | |
| ```none | |
| fuchsia_component_test_collection:{{ '<var>' }}child-name{{ '</var>' }}/{{ '<var>' }}component-name{{ '</var>' }} | |
| ``` | |
| The moniker consists of the following elements: | |
| * `child-name`: An auto-generated name for the realm's collection, created | |
| by the Realm Builder library. Obtained by calling the `child_name()` | |
| function of the constructed realm. | |
| * `component-name`: The "Component name" parameter provided to the | |
| `Add Component` component when [constructing the realm](#construct-realm). | |
| To obtain the child name invoke the following method on the constructed Realm: | |
| * {Rust} | |
| ```rust | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/rust/src/lib.rs" region_tag="get_child_name_rust" adjust_indentation="auto" %} | |
| ``` | |
| * {C++} | |
| ```cpp | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/components/realm_builder/cpp/sample.cc" region_tag="get_child_name_cpp" adjust_indentation="auto" %} | |
| ``` | |
| ## Troubleshooting {#troubleshoot} | |
| ### Invalid capability routes {#invalid-routes} | |
| The add route function cannot validate if a capability is properly offered | |
| to the created realm from the test component. | |
| If you attempt to route capabilities with a source of `parent` without a | |
| corresponding [offer][offer], requests to open the capability will not resolve | |
| and you will see error messages similar to the following: | |
| ```none {:.devsite-disable-click-to-copy} | |
| [86842.196][klog][E] [component_manager] ERROR: Required protocol `fidl.examples.routing.echo.Echo` was not available for target component `/core/test_manager/tests:auto-10238282593681900609/test_wrapper/test_root/fuchsia_component_test_ | |
| [86842.197][klog][I] collection:auto-4046836611955440668/echo-client`: An `offer from parent` declaration was found at `/core/test_manager/tests:auto-10238282593681900609/test_wrapper/test_root/fuchsia_component_test_colle | |
| [86842.197][klog][I] ction:auto-4046836611955440668` 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 test capabilities](#routing-from-test). | |
| ## Language feature matrix {#language-feature-matrix} | |
| <table> | |
| <tr> | |
| <th></th> | |
| <th>Rust</th> | |
| <th>C++</th> | |
| </tr> | |
| <tr> | |
| <td>Legacy components</td> | |
| <td><span class="compare-yes"></span></td> | |
| <td><span class="compare-yes"></span></td> | |
| </tr> | |
| <tr> | |
| <td>Mock components</td> | |
| <td><span class="compare-yes"></span></td> | |
| <td><span class="compare-yes"></span></td> | |
| </tr> | |
| <tr> | |
| <td>Override configuration values</td> | |
| <td><span class="compare-yes"></span></td> | |
| <td><span class="compare-yes"></span></td> | |
| </tr> | |
| <tr> | |
| <td>Manipulate component declaration</td> | |
| <td><span class="compare-yes"></span></td> | |
| <td><span class="compare-no"></span></td> | |
| </tr> | |
| </table> | |
| [cap-routes]: /docs/concepts/components/v2/capabilities/README.md#routing | |
| [children]: https://fuchsia.dev/reference/cml#children | |
| [collection]: https://fuchsia.dev/reference/cml#collections | |
| [component-urls]: /docs/reference/components/url.md | |
| [environment]: https://fuchsia.dev/reference/cml#environments | |
| [expose]: https://fuchsia.dev/reference/cml#expose | |
| [fragment-only-url]: /docs/reference/components/url.md#relative-fragment-only | |
| [namespaces]: /docs/concepts/process/namespaces.md | |
| [offer]: https://fuchsia.dev/reference/cml#offer | |
| [realm-builder-shard]: /sdk/lib/sys/component/realm_builder_base.shard.cml | |
| [realms]: /docs/concepts/components/v2/realms.md | |
| [resolver]: /docs/concepts/components/v2/capabilities/resolver.md | |
| [runner]: /docs/concepts/components/v2/capabilities/runner.md | |
| [shard-includes]: https://fuchsia.dev/reference/cml#include | |
| [test-runner]: /docs/development/testing/components/test_runner_framework.md#test-runners | |
| [use]: https://fuchsia.dev/reference/cml#use |