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.

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]:

// 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:

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:

fx add-test //path/to/your/tests:reverser_trf_integration_test
fx test //path/to/your/tests