Tests as components

Caution: This document describes manifests for components v1. The old architecture (components v1) implemented by appmgr is still in use, but will be removed once the transition to the new architecture is complete.

This document outlines the way for developers to configure their tests to run as Fuchsia components. This document is for developers working inside the Fuchsia source tree (fuchsia.git). The workflow described in this document is not suitable for the Fuchsia SDK-based developers.

The exact GN invocations used to produce a test may vary between different classes of tests and different languages. This document assumes that test logic is being built somewhere and the test binary is to be built to run as a Fuchsia component. For C++ and Rust, this would be the executable file that the build produces. (For information on writing tests in Rust, see Testing Rust code.)

For an example setup of a test component, see //examples/hello_world/rust.

Fuchsia test environments

Tests on Fuchsia can run either as standalone executables (for example, host tests) or as components. Standalone executables are invoked within the environment the test runner is in, whereas components executed in a test runner run in a hermetic environment. These hermetic environments are fully separated from the host services. Test manifests determine whether new instances of services should be started in this environment, or services from the host should be plumbed in to the test environment.

Packaging a test as a component

To package a test as a component, you need to create the following:

Note: When writing simple unit tests that do not depend on any facets, features, or services in Fuchsia, you can create a test package using the fuchsia_unittest_package template. This template lets the build system automatically generate a component manifest for these tests.

Component manifest for a test

A component manifest informs the component framework how to run a component. In this case, it explains how to run the test binary. A component manifest file (.cmx) is typically located in a meta directory next to the BUILD.gn file:

<your_test_directory>
  ├── BUILD.gn
  ├── meta
  │   └── <component_manifest_file>
  ...

When a package is built, it includes the component manifest file under a top level directory, which is also called meta.

The simplest possible component manifest for running a test may look like the following:

{
    "program": {
        "binary": "test/hello_world_rust_bin_test"
    }
}

When you run the component above, it invokes the test/hello_world_rust_bin_test binary in the package.

However, the example manifest above may be inadequate for many use cases because the program running under this manifest has a very limited set of capabilities. For instance, there is no mutable storage available for this program and it cannot access any services in Fuchsia.

Additional capabilities

The manifest can include additional content to request more capabilities. As an alternative to the prior example, the following example provides the component access to storage at /cache and allows the component to write to the system log.

{
    "include": [ "sdk/lib/diagnostics/syslog/client.shard.cmx" ],
    "program": {
        "binary": "test/hello_world_rust_bin_test"
    },
    "sandbox": {
        "features": [ "isolated-cache-storage" ]
    }
}

Note: The above syntax is only available for in-tree development. This is tracked in fxbug.dev/64207. Out of tree developers should copy the snippet shown below instead.

{
    "program": {
        "binary": "test/hello_world_rust_bin_test"
    },
    "sandbox": {
        "features": [ "isolated-cache-storage" ],
        "services": [ "fuchsia.logger.LogSink" ]
    }
}

Test components can also have new instances of services created inside their test environment, thus isolating the impact of the test from the host. In the following example, the service available at /svc/fuchsia.example.Service is be handled by a brand new instance of the service referenced by the URL:

{
    "program": {
        "binary": "test/hello_world_rust_bin_test"
    },
    "facets": {
        "fuchsia.test": {
            "injected-services": {
                "fuchsia.example.Service": "fuchsia-pkg://fuchsia.com/example#meta/example_service.cmx"
            }
        }
    },
    "sandbox": {
        "services": [
            "fuchsia.example.Service"
        ]
    }
}

For more information on component manifests, see Component manifests.

Package build rules for a test component

Once you have a component manifest, you can now add GN build rules in BUILD.gn to create a package for the test component.

The following example produces a new package named hello-world-rust-tests that contains the artifacts necessary to run a test component:

Note: The legacy GN templates for packages (package.gni and test_package.gni) are being deprecated. See New GN templates for components v1 for details.

import("//src/sys/build/fuchsia_unittest_package.gni")

fuchsia_unittest_component("hello-world-rust-test-component") {
  executable_path = "test/hello_world_rust_bin_test"
  component_name = "hello-world-rust-test"
  deps = [ ":bin" ]
}

fuchsia_test_package("hello-world-rust-tests") {
  test_components = [
    ":hello-world-rust-test-component",
  ]
}

The example above requires that the :bin target produces a test binary named hello_world_rust_bin_test. The fuchsia_unittest_package template requires that meta/${TEST_NAME}.cmx exists and that the destination of the test binary matches the target name. In the example, this means that meta/hello_world_rust_bin_test.cmx must exist.

This template produces a package in the same way that the fuchsia_package template does, but it has extra checks in place to ensure that the test is set up properly. For more information, see Test packages.

New GN templates for components v1

The legacy GN templates for packages (test_package.gni and package.gni) are being deprecated in favor of the following new templates:

For instance, the examples below demonstrate how you can rewrite the test_package template's build rules using the fuchsia_unittest_package template:

  • {test_package}

    import("//build/test/test_package.gni")
    
    test_package("hello-world-rust-tests") {
      deps = [
        ":bin",
      ]
      tests = [
        {
          name = "hello_world_rust_bin_test"
        }
      ]
    }
    
  • {fuchisa_unittest_package}

```none
import("//src/sys/build/fuchsia_unittest_package.gni")

fuchsia_unittest_component("hello-world-rust-test-component") {
  executable_path = "test/hello_world_rust_bin_test"
  component_name = "hello-world-rust-test"
  deps = [ ":bin" ]
}

fuchsia_test_package("hello-world-rust-tests") {
  test_components = [
    ":hello-world-rust-test-component",
  ]
}
```

For more examples on these templates, see Test component.