blob: 0ba30d187ac0029ba50dae9818f8efec289585fa [file] [log] [blame] [view]
# FIDL Tests & GN
This document standardizes the way we define and organize FIDL tests in the GN
build system, respecting the following goals:
* **Name consistently**. If Rust uses `fx test fidl_rust_conformance_tests`,
then Go should use `fx test fidl_go_conformance_tests`. Consistent and
predictable naming provides a better developer experience.
* **Run what you need**. The testing workflow should make it easy to run a
single test component without building or running anything extra.
* **Run on host**. Where possible, tests should support running on host
(non-Fuchsia), where the edit-build-run cycles are typically much quicker.
* **Follow best practices**. We should follow Fuchsia best practices around
[using `fx test`][Run Fuchsia tests],
[building components][Building components], etc.
## Terminology
This document uses the following terminology:
* **target**: a [GN target] defined in a BUILD.gn file
* **toolchain**: see `fx gn help toolchain`
* **host**: a developer's platform, specifically linux or mac
* **device**: a Fuchsia platform, either physical or emulated (i.e. qemu)
* **package**: a [Fuchsia package]; the unit of distribution in
Fuchsia
* **component**: a [Fuchsia component]; the unit of executable software in
Fuchsia
## Naming {#naming}
General guidelines:
* Use underscores, not hyphens.
* End names with the plural `_tests` rather than the singular `_test`.
* Use full, descriptive, unique names for packages, components, and binaries.
The last point means preferring full names like `fidl_rust_conformance_tests`
over contextual names like `conformance_tests`. It might seem verbose and
redundant to repeat "fidl" and "rust" at the directory, package, component, and
binary level. But the fact is that these names must be unique, and it is better
to make them unique in a consistent way rather than remembering odd rules like
`fidl-bindings-test` is for Dart and `fidl-test` is for C.
Names should use the following scheme, joining parts with underscores:
> _tool_ \[ _bindings_ ] \[ _category_ \[ _subcategory_ ] ] **tests**
Where _tool_ is one of:
* **fidl**: FIDL runtime support
* **fidlc**: FIDL compiler frontend
* **fidlgen**: FIDL compiler backend
* **gidl**, **measure_tape**, etc.: other tools
And the other parts are:
* _bindings_
* One of **c**, **cpp**, **cpp_wire**, **hlcpp**, **rust**, **go**,
**dart**
* _category_, _subcategory_
* Example categories: **conformance**, **types**, **parser**, **lib**
* Do _not_ use: **frontend**, **backend**, **bindings** (_tool_
distinguishes these)
## Hierarchy
Every BUILD.gn file that defines tests should include a `"tests"` group:
```gn
group("tests") {
testonly = true
deps = [ ... ] # not public_deps
}
```
If the directory ends in "tests", and the BUILD.gn file only defines test
targets, the group should instead match the directory name. For example,
foo_tests/BUILD.gn could use `group("foo_tests")`. This enables the GN label
shorthand `//path/to/foo_tests`, equivalent to `//path/to/foo_tests:foo_tests`.
These groups are aggregated in "tests" groups of BUILD.gn files in parent
directories. The root "tests" group (for some portion of the codebase, e.g.
src/lib/fidl/BUILD.gn) should be included in bundles/fidl/BUILD.gn. This enables
`fx set ... --with //bundles/fidl:tests` to include all FIDL tests in the build.
(The tests are also run in CQ because `//bundles/buildbot/core` includes
`//bundles/fidl:tests`.)
## Binary names
Normally test binary names are based on the target name. For example, a
`test("some_tests") { ... }` target would produce a `some_tests` binary.
However, for a single test you often need multiple targets (source sets,
components, packages, etc.) with unique names. Therefore, the examples in this
document use target names like `some_tests_bin` and override the binary name
using the `output_name` parameter:
```gn
test("some_tests_bin") {
output_name = "some_tests"
...
}
```
This also works for `rustc_test`, `go_test`, etc.
## Device tests
Assume we have a `:fidl_foo_tests_bin` target that produces a `fidl_foo_tests`
binary. To wrap this in a package, use `fuchsia_unittest_package`:
```gn
import("//build/components.gni")
fuchsia_unittest_package("fidl_foo_tests") {
deps = [ ":fidl_foo_tests_bin" ]
}
```
We can now run the test by package name or component name (they are the same)
with `fx test fidl_foo_tests`.
**Use a separate package for each test.** If unrelated test components are
bundled in one package, running one of the tests causes the whole package to be
rebuilt. You should only bundle multiple test components in a package if they
are meant to be tested together, e.g. a client and server integration test. See
[Complex topologies and integration testing] for examples.
If your test requires any component features, services, etc. beyond the
`fuchsia_unittest_component` defaults, you must write a component manifest file:
```gn
# BUILD.gn
import("//build/components.gni")
fuchsia_unittest_package("fidl_foo_tests") {
manifest = "meta/fidl_foo_tests.cml"
deps = [ ":fidl_foo_tests_bin" ]
}
# meta/fidl_foo_tests.cml
{
program: {
"binary": "bin/fidl_foo_tests"
},
use: [
{
protocol: [
"fuchsia.logger.LogSink", # some example services
"fuchsia.process.Launcher"
]
}
]
}
```
For more information on package and component templates, see [Building
components].
## Host tests
Assume we have a `:fidl_bar_tests_bin` target that produces a `fidl_bar_tests`
binary. We must **ensure that GN is in `$host_toolchain` when it reaches that
target**, otherwise it will try to build it for Fuchsia:
```gn
groups("tests") {
testonly = true
deps = [ ":fidl_bar_tests_bin($host_toolchain)" ]
}
```
(Always put `($host_toolchain)` in the BUILD.gn file's `tests` group, not in
//bundles/fidl:tests.)
This will create a test_spec entry named `host_x64/fidl_bar_tests`, which will
end up in out/default/tests.json:
```json
{
"command": [ "host_x64/fidl_bar_tests", "--test.timeout", "5m" ],
"cpu": "x64",
"label": "//PATH/TO/BAR:fidl_bar_tests_bin(//build/toolchain:host_x64)",
"name": "host_x64/fidl_bar_tests",
"os": "linux",
"path": "host_x64/fidl_bar_tests",
"runtime_deps": "host_x64/gen/PATH/TO/BAR/fidl_bar_tests_bin.deps.json"
}
```
Running `fx test fidl_bar_tests` works because of the "name" field in
tests.json.
## Host/Device tests
Tests that run both on host and device fall in two categories. In the first
category, the test target simply builds under either toolchain. For example:
```gn
import("//build/components.gni")
rustc_test("fidl_rust_conformance_tests_bin") {
output_name = "fidl_rust_conformance_tests" # host test name
...
}
fuchsia_unittest_package("fidl_rust_conformance_tests") { # device test name
deps = [ ":fidl_rust_conformance_tests_bin" ]
}
group("tests") {
testonly = true
deps = [
":fidl_rust_conformance_tests_bin($host_toolchain)",
":fidl_rust_conformance_tests",
]
}
```
We can now run the test both ways:
* on device: `fx test fidl_rust_conformance_tests --device`
* on host: `fx test fidl_rust_conformance_tests --host`
In the second category, the device and host tests share source code, but they
are sufficiently different that they must be defined by separate targets. This
requires wrapping the host test definition in `if (is_host) { ... }` to prevent
GN complaining about multiple targets producing the same output. For example:
```gn
import("//build/components.gni")
source_set("conformance_test_sources") {
...
}
test("fidl_hlcpp_conformance_tests_bin") {
output_name = "fidl_hlcpp_conformance_tests"
...
deps = [
":conformance_test_sources",
...
]
}
if (is_host) {
test("fidl_hlcpp_conformance_tests_bin_host") {
output_name = "fidl_hlcpp_conformance_tests" # host test name
...
deps = [
":conformance_test_sources",
...
]
}
}
fuchsia_unittest_package("fidl_hlcpp_conformance_tests") { # device test name
deps = [ ":fidl_hlcpp_conformance_tests_bin" ]
}
group("tests") {
testonly = true
deps = [
":fidl_hlcpp_conformance_tests_bin_host($host_toolchain)",
":fidl_hlcpp_conformance_tests",
]
}
```
Now, we can run the test both ways:
* on device: `fx test fidl_hlcpp_conformance_tests --device`
* on host: `fx test fidl_hlcpp_conformance_tests --host`
## Rust unit tests
Rust libraries can be defined like this:
```gn
rustc_library("baz") {
with_unit_tests = true
...
}
```
This automatically creates a `baz_test` target that builds a `baz_lib_test`
binary. **Do not use this**, for two reasons:
1. The [naming guidelines](#naming) require a `_tests` suffix, not `_test`.
2. It can be confusing and might be [deprecated][with_unit_tests] in the
future.
Instead of `with_unit_tests`, write a separate `rustc_test` target with an
appropriate name:
```gn
rustc_library("baz") {
...
}
rustc_test("fidl_baz_tests") {
...
}
```
## Grouping
Suppose we have the following test structure:
* FIDL Rust
* Device
* Conformance
* Integration
* Host
* Conformance
We should have test targets for the leaves:
* `fx test fidl_rust_conformance_tests`
* `fx test fidl_rust_integration_tests`
We should **not** make additional packages for running various subsets of the
tests. Using `fx test`, we can already
* run all tests: `fx test //path/to/fidl/rust`
* run all device tests: `fx test //path/to/fidl/rust --device`
* run all host tests: `fx test //path/to/fidl/rust --host`
## References
* [Source code layout]
* [Building components]
* [Run Fuchsia tests]
* [Fuchsia component manifest]
* [Fuchsia package URLs]
<!-- xrefs -->
[GN target]: https://gn.googlesource.com/gn/+/HEAD/docs/language.md#Targets
[Fuchsia package]: /docs/concepts/packages/package.md
[Fuchsia component]: /docs/concepts/components/v2
[Run Fuchsia tests]: /docs/development/testing/run_fuchsia_tests.md
[Fuchsia component manifest]: /docs/concepts/components/v2/component_manifests.md
[Fuchsia package URLs]: /docs/concepts/packages/package_url.md
[Source code layout]: /docs/development/source_code/layout.md
[Building components]: /docs/development/components/build.md
[Complex topologies and integration testing]: /docs/development/testing/components/integration_testing.md
[with_unit_tests]: https://fuchsia.googlesource.com/fuchsia/+/9d9f092f2b30598c3929bd30d0058d4e052bb0f4/build/rust/rustc_library.gni#91