The ffx_e2e_emu library
(//src/developer/ffx/lib/e2e_emu) implements helpers for writing end-to-end
(e2e) tests for ffx subtools and libraries.
It includes:
- An isolated emulator (
IsolatedEmulator) started with a Fuchsia system image viaffx emu - A hermetic execution environment for
ffx(viaffx_isolate) - Automatic system log streaming and package repository server setup
- Helper methods for executing
ffxandsshcommands against the isolated emulator - GN templates (
ffx_e2e_test,ffx_e2e_product_bundle, andffx_e2e_package_repository) in//src/developer/ffx/lib/e2e_emu/ffx_e2e_test.gni
Usage
Subtool and library e2e tests are defined with the ffx_e2e_test GN template
and are typically added to a host_tests group in the subtool or library's
BUILD.gn.
In BUILD.gn:
import("//build/host.gni")
import("//src/developer/ffx/lib/e2e_emu/ffx_e2e_test.gni")
if (is_host) {
ffx_e2e_test("ffx_<something>_e2e_tests") {
edition = "2024"
source_root = "tests/e2e.rs"
sources = [ source_root ]
deps = [
"//src/lib/fuchsia",
"//third_party/rust_crates:anyhow",
]
}
group("host_tests") {
testonly = true
deps = [ ":ffx_<something>_e2e_tests($host_toolchain)" ]
}
}
In tests/e2e.rs:
use anyhow::Result;
use ffx_e2e_emu::IsolatedEmulator;
#[fuchsia::test(logging = true)]
async fn my_test() -> Result<()> {
let emu = IsolatedEmulator::start("my-test").await?;
let output = emu.ffx_output(&["version"]).await?;
assert!(!output.is_empty());
Ok(())
}
Customizing the system image and package repository
By default, ffx_e2e_test uses a minimal default product bundle and an empty
package repository.
Custom Product Bundle: If your test requires specific platform configuration or base packages, define a custom bundle with
ffx_e2e_product_bundleand passproduct_bundle = ":<label>"toffx_e2e_test:ffx_e2e_product_bundle("product_bundle") { name = "my_custom_test_product" product_configuration = { platform = { build_type = "eng" } base_packages = [ { package_target = "//path/to/my:package" }, ] } }Custom Package Repository: If your test needs to resolve packages from a repository server at runtime rather than including them in base, define a repository with
ffx_e2e_package_repositoryand passpackage_repository = ":<label>($target_toolchain)"toffx_e2e_test:if (is_fuchsia) { ffx_e2e_package_repository("package_repo") { packages = [ { label = "//path/to/my:package" }, ] } }
Low-Level Emulators
For tests that specifically need a low-level emulator brought up with an empty
disk (for example, testing bootloader and fastboot flashing via Gigaboot before
a Fuchsia image is installed), //src/developer/ffx/tests/target:ffx_testing
provides ffx_testing::Emu.
Emu::start spawns QEMU directly via run-zircon with an empty UEFI disk and
exposes the emulator serial stream. For an example, see
ffx_target_test.
/!\ This emulator is brought up with an empty disk /!\
This is by design. To e2e test with a fuchsia image use the ffx_e2e_emu
library under //src/developer/ffx/lib/e2e_emu.
use ffx_testing::Emu;
let emu = Emu::start(&ctx);
// NOTE: If using serial, make sure to drain or close the serial stream when no longer in use.
// Failure to do so will result in QEMU halting.
{
let serial = emu.serial().await;
// ...
}
// Dropping or letting the emu go out of scope cleans up the emulator.