blob: 26ece14420dbb62945907b5e31906168e15498ae [file] [view]
<!-- Copyright 2026 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. -->
# TRF Codegen: Declarative Integration Testing Framework
The TRF (Test Realm Factory) Codegen suite automates the boilerplate
involved in setting up integration tests using the Realm Factory pattern
in Fuchsia. It aims to generate typed Rust harnesses that eliminate
manual FIDL routing overhead, providing developers with a streamlined,
ergonomic testing experience.
It eliminates boilerplate in component integration tests by automatically
parsing Rust test definitions at build time to generate:
- **FIDL Mock Control Libraries** (`fuchsia.trf.mockcontrol`) for
inspecting and manipulating mock component state.
- **Injectable Universe (IU) Mock Host Components** serving
protocol mocks with isolated state.
- **Component Manifests (`.cml`)** for the Test Root, Test Driver,
and Injectable Universe components.
- **Capability Topology Routing** linking Component Under Test (CUT),
mock components, and the test driver automatically.
## Component Architecture
This directory is the root of the TRF Codegen infrastructure. Currently,
it holds foundational dependencies that the generated code relies upon:
- **`fidl/`**: Contains the protocol definitions (e.g.,
`fuchsia.trf.factory`) that define the contract for configuring and
launching a test realm dynamically via a factory component.
- **`lib/runtime/`**: Provides the runtime Rust library
(`trf_codegen_runtime`). Generated tests use this library's
capabilities (such as `TestRealm`) as a common, high-level harness
wrapper to manage component capabilities, connect to mock controls,
and stream lifecycle events.
---
## User's Guide
### 1. Define the GN Build Target
Import `//src/testing/trf_codegen/trf_test.gni` in your `BUILD.gn` file
and define a `trf_test` target specifying the Rust test source file and
the target Component Under Test (CUT) dependency.
```gn
import("//src/testing/trf_codegen/trf_test.gni")
if (is_fuchsia) {
trf_test("reverser_trf_integration_test") {
source = "src/reverser_trf_tester.rs"
cut = [ "//examples/components/reverser" ]
# Optional: List of source files containing TRF mock implementations
mocks = [ "src/mocks.rs" ]
# Optional: Archive test in the Compatibility Test Framework (CTF)
# preserving it for backwards compatibility testing on branch cuts
is_ctf = true
}
}
```
### 2. Write the Integration Test
In your Rust test source file, import the TRF macros and runtime harness.
Annotate test entry points with `#[trf_test]`:
```rust
// Copyright 2026 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.
use anyhow::Error;
use fidl_fuchsia_examples_reverser as freverser;
use trf_codegen_macro::trf_test;
// Note: The `TestRealm` argument type is mapped to a generated wrapper struct.
#[trf_test(config(switch_case = false, use_replacer = true), mocks(MockReplacer))]
pub async fn test_reverser_trf_mock(realm: &TestRealm) -> Result<(), Error> {
// Call control proxy methods directly on the realm
// (Generated only if `mocks(MockReplacer)` is specified)
realm.set_replacement("Hello".to_string(), "Greetings".to_string()).await?;
// Connect to protocols exposed by the Component Under Test (CUT)
let client = realm.connect_to_protocol::<freverser::ReverserMarker>()?;
let input = "Hello Fuchsia TRF!";
let output = client.reverse(input).await?;
assert_eq!(output, "!FRT aishcuF sgniteerG");
Ok(())
}
```
### 3. Define Protocol Mocks (Optional)
If your Component Under Test relies on external protocols, you can author mock implementations to satisfy them. Mocks are placed in dedicated Rust source files (e.g. `src/mocks.rs`) and passed via the `mocks` argument of the `trf_test` GN template. Use `#[trf::mock]` to implement the mocked protocol, and `#[trf::control]` to define a control channel for the test driver to manipulate the mock's local state:
```rust
use fidl_fuchsia_examples_reverser as freverser;
use futures::StreamExt;
use std::sync::Mutex;
use trf_codegen_macro as trf;
#[trf::mock(protocol = "fuchsia.examples.reverser.Replacer")]
pub struct MockReplacer {
// Internal mock state must be safely shared across concurrent requests
from: Mutex<String>,
to: Mutex<String>,
}
impl MockReplacer {
pub fn new() -> Self {
Self { from: Mutex::new(String::new()), to: Mutex::new(String::new()) }
}
// 1. Define the control interface for the Test Driver to drive the mock
#[trf::control]
pub fn set_replacement(&self, from: String, to: String) {
*self.from.lock().unwrap() = from;
*self.to.lock().unwrap() = to;
}
// 2. Define the protocol server loop served to the CUT.
pub async fn serve_replacer(&self, mut stream: freverser::ReplacerRequestStream) -> Result<(), anyhow::Error> {
while let Some(Ok(req)) = stream.next().await {
match req {
freverser::ReplacerRequest::Replace { value, responder } => {
let from = self.from.lock().unwrap().clone();
let to = self.to.lock().unwrap().clone();
let result = if !from.is_empty() { value.replace(&from, &to) } else { value };
let _ = responder.send(&result);
}
}
}
Ok(())
}
}
```
### 4. Run the Test
Execute your integration test using standard `fx` workflow commands:
```bash
fx add-test //path/to/your/tests:reverser_trf_integration_test
fx test //path/to/your/tests
```