blob: 28ad8466dbc52b67fabd2475b744d51a2fe81907 [file] [view]
# Tests and test suites
[TOC]
## Overview
This page provides style and best practices for defining and structuring tests
and `test_suite()` targets in [`BUILD.bazel` files][build-files-guide] and
connecting them to the GN build graph. The guidance on this page is in addition
to the [common Bazel style guide and best practices][common-guide] and the
[`BUILD.bazel` files style guide][build-files-guide]. For instructions on how
to define, build, and run host and device test targets, see
[Bazel tests][bazel-tests-guide].
This page is part of the
[Bazel style guide and best practices][style-guide-landing-page].
## Test suite structure {:#test-suite-structure}
In Fuchsia's Bazel build graph, tests are organized and connected to the build
in three layers:
1. **Individual packages**: Each `BUILD.bazel` file defines private `*_test`
targets and exposes them via package `test_suite()` targets named `"tests"`
and/or `"host_tests"` (see [Defining tests and test suites in individual
packages][individual-packages]).
2. **Aggregation packages**: Dedicated packages for an area or subsystem group
the package `test_suite()` targets from individual packages into
[aggregation `test_suite()` targets][aggregation-test-suite-targets] (for
example, `"bluetooth_tests"`).
3. **GN build graph integration**: While GN drives the overall build,
`bazel_test_suite()` targets in `BUILD.gn` reference the
[aggregation `test_suite()` targets][aggregation-test-suite-targets] in the
same directory so the tests are included in `tests.json` (see [Connecting
Bazel test suites to the GN build graph][connecting-to-gn]).
Individual tests MUST NOT be referenced outside the package in which they are
defined, and packages do not recursively include tests or `test_suite()`
targets from their subpackages. See [Why only expose `test_suite()`
targets?][why-only-expose-test-suite-targets] for the rationale behind this
structure.
### Example
Note: This example is currently hypothetical.
**TODO([https://fxbug.dev/570694755][fxbug-570694755]{:.external}):** Update
this after the Bluetooth tests are updated.
`//src/connectivity/bluetooth/lib/packet-encoding/BUILD.bazel` (individual
package):
```bazel
test_suite(
name = "tests",
tests = [], # Include all tests in the package.
visibility = ["//src/connectivity/bluetooth:__pkg__"],
)
# Private test target(s) automatically included in ":tests" above.
...
```
`//src/connectivity/bluetooth/BUILD.bazel` (aggregation package):
```bazel
test_suite(
name = "bluetooth_tests",
tests = [
"//src/connectivity/bluetooth/lib/packet-encoding:tests",
...
],
)
```
`//src/connectivity/bluetooth/BUILD.gn` (GN integration):
```gn
import("//build/bazel/bazel_test_suite.gni")
bazel_test_suite("bazel_tests") {
target_tests = [ "//src/connectivity/bluetooth:bluetooth_tests" ]
}
group("tests") {
testonly = true
deps = [
":bazel_tests",
...
]
}
```
## Defining and structuring Bazel tests and test suites
### Target names and visibility {:#target-names-and-visibility}
* **Target names**: The names of test targets always end in `"_test"`. The names
of Bazel `test_suite()` targets should be or end in `"tests"` or
`"host_tests"` as appropriate.
* **Test target visibility**: Individual tests MUST NOT be referenced outside
the package in which they are defined. Test targets must always have
package-private visibility, which is generally the default (see [Target
visibility][target-visibility]).
* **`test_suite()` target visibility**: `test_suite()` targets should only be
visible to package(s) containing the
[aggregation `test_suite()` targets][aggregation-test-suite-targets] that
depend on them (in their `tests` attribute). Always use `:__pkg__` rather
than `:__subpackages__`.
* **Referencing tests and `test_suite()` targets**: Tests and `test_suite()`
targets must only be referenced by `test_suite()` targets (in their `tests`
attribute). Do NOT put them in a `filegroup()` or similar.
### Platform and CPU architecture compatibility {:#platform-compatibility}
#### Target platform compatibility (target_compatible_with)
Test targets can and should specify `target_compatible_with` as appropriate
(for example, `target_compatible_with = HOST_OS_CONSTRAINTS` on host tests; see
[`target_compatible_with`][target-compatible-with]).
However, do not rely on [Bazel test compatibility][bazel-test-compatibility] to
filter test targets and only run compatible ones. Specifically, do not group
tests for different platforms in a single `test_suite()` (or `test_suite()`
targets containing tests for different platforms in a single `test_suite()`).
Note: **TODO([https://fxbug.dev/540003943][fxbug-540003943]{:.external}):**
Formalize this.
It is TBD whether `test_suite()` targets should specify
`target_compatible_with`.
Note: **TODO([https://fxbug.dev/540003943][fxbug-540003943]{:.external}):**
Decide whether `test_suite()` targets should specify `target_compatible_with`
and update this text.
#### CPU architecture {:#cpu-architecture}
Avoid defining separate tests or `test_suite()` targets for, for example, each
CPU architecture where possible. Ideally, a single test target can be built for
all CPU architectures.
In rare cases, there may be CPU architecture-specific tests, such as unit tests
for a SIMD library written in assembly. In these cases, seek guidance from the
Build team. (Once we have some experience with these scenarios, we can update
this text.)
### Defining tests and test suites in individual packages {:#packages}
#### Exposing package test_suite() targets
Each Bazel package (`BUILD.bazel` file) should expose one or more package
`test_suite()` targets that depend on the tests defined in that package (in the
single `BUILD.bazel` file, not in any of its subpackages).
These targets should be named `"tests"` or `"host_tests"` depending on the
target platform. All such `test_suite()` targets should be the first targets in
a `BUILD.bazel` file, ideally immediately after `load()` and `package()`
statements.
Note: We can consider expanding the list of standard target names if a need
arises, such as for differentiation by various
[aggregation `test_suite()` targets][aggregation-test-suite-targets]. There may
also be exceptions such as in the [SIMD library example
above][cpu-architecture]; these should be discussed with the Build team.
#### Including all tests in the package
All tests defined in a Bazel package must be a dependency of one of the
`test_suite()` targets in the same package. (In the future, this may change to
"at least one".) This includes tests defined by macros used to define non-test
targets.
Note: **TODO:** Implement a query-based test to ensure this.
If all tests in a package should be in a given `test_suite()`, specify an empty
`tests` attribute with a comment noting that all tests in the package are
included:
```bazel
test_suite(
name = "tests",
tests = [], # Include all tests in the package.
visibility = [...],
)
```
When possible, this is easier to maintain, requires one less step when adding
tests, and ensures that a test is not missed.
Note: In rare cases where a test target depends on another test target, it may
be necessary to add `tags = ["manual"]` to the inner test target.
#### Defining a default test_suite() in all packages
Where possible, all `BUILD.bazel` files should have a `test_suite()` named
`"tests"` or `"host_tests"` with an empty `tests` attribute (`tests = []`)
regardless of whether they explicitly define tests. Each such `test_suite()`
should be referenced from an appropriate
[aggregation `test_suite()` target][aggregation-test-suite-targets].
This ensures that any tests added to the package in the future - either
explicitly or by a macro - will be included in the build graph. If we can
enforce this, it also prevents forgetting to add a test to the build graph.
The primary exceptions to this are packages that contain tests for both Fuchsia
and host because it is not currently possible to use the same test target in
both cases and thus the empty `tests` attribute mechanism cannot be used.
Note: **TODO:** Consider how we might be able to use the same set of test
targets.
#### Modifying a test_suite() with an empty tests attribute
When modifying an existing `test_suite()` whose `tests` attribute is either
empty or not specified, you MUST ensure that all tests defined by the package
are explicitly added to this or another `test_suite()` in the package. The only
reason this should occur is if a package's tests are being divided among
multiple `test_suite()` targets.
#### Do not depend on subpackages
Do not depend on tests or `test_suite()` targets in the package's subpackages.
The exception is
[aggregation `test_suite()` targets][aggregation-test-suite-targets], but a
single package should generally not have both types of `test_suite()` targets.
This is a departure from the common pattern in GN where `group("tests")`
depended on all such targets in its subdirectories, which recursively did the
same. We do NOT want to repeat that pattern in Bazel.
### Aggregation test_suite() targets {:#aggregation-test-suite-targets}
These `test_suite()` targets group package `test_suite()` targets from
individual packages (for example, for an area or subsystem) and should have
names that describe their scope and/or purpose. They should still end in
`"tests"` or `"host_tests"` as appropriate. There may be more than one of each
such target. As a hypothetical example, `"bluetooth_tests"` might apply to all
products while `"bluetooth_le_tests"` might include additional tests to run on
products that support Bluetooth LE.
In general, packages (`BUILD.bazel` files) containing such
[aggregation `test_suite()` targets][aggregation-test-suite-targets] should not
themselves define other targets, especially test targets. For example,
`//src/connectivity/bluetooth/`. (Note also that the `BUILD.gn` file only
defines `group()` targets.)
### Depending on the aggregation targets {:#depending-on-aggregation-targets}
In the future, this section will describe optional further aggregation and
inclusion in the test bundles for each product.
For now, the existing dependencies to the GN tests `group()` targets that
depend on the [`bazel_test_suite()` targets][connecting-to-gn] are used.
### Rust unit tests {:#rust-unit-tests}
In GN, the `rustc_*()` templates accept a `with_unit_tests` parameter that when
true automatically generates unit test targets from the tests in the source
files.
Note: **TODO:** Decide whether we want to follow this pattern in Bazel, and if
not, recommend a pattern.
Note: **TODO:** If we decide to use this pattern, stop passing the macros'
`visibility` attribute to the test targets.
## Connecting Bazel test suites to the GN build graph {:#connecting-to-gn}
Note: This section applies while GN is still driving the overall build.
Targets defined with the `bazel_test_suite()` GN template should only reference
[aggregation `test_suite()` targets][aggregation-test-suite-targets] defined in
the same directory (in a `BUILD.bazel` file next to the `BUILD.gn` file). They
must not reference individual tests.
Each [aggregation `test_suite()` target][aggregation-test-suite-targets] must
be referenced in the appropriate field of a target defined by the
`bazel_test_suite()` GN template in the same directory (in a `BUILD.gn` file
next to the `BUILD.bazel` file) so that its tests are included in `tests.json`.
Use full Bazel labels starting with `"//"` rather than relative `":"` labels
(for example, `"//src/connectivity/bluetooth:bluetooth_tests"`), and reference
the targets in the GN template's parameters as follows:
* Targets whose names end in `"host_tests"`: `host_tests`
* All others (ending in `"tests"`): `target_tests`
## Appendix: Why only expose test_suite() targets? {:#why-only-expose-test-suite-targets}
Advantages of only exposing `test_suite()` targets rather than individual tests
include:
* `visibility` only needs to be specified for the `test_suite()` target(s) and
not for every individual test target.
* This is especially relevant for macros such as Fuchsia's `rustc_*()` and
`ffx_plugin()` that support `with_host_unit_tests` and/or `with_unit_tests`
(see [Rust unit tests][rust-unit-tests]) because the `visibility` of the test
targets cannot be specified independent of the production target. If the
individual tests were referenced outside the Bazel package, the `visibility`
of the non-test target would need to include the Bazel package that depends on
the test.
* Which tests exist and what they are named is abstracted away from the
[aggregation `test_suite()` targets][aggregation-test-suite-targets].
* There is no need for `OWNERS` approval for the package containing the
[aggregation `test_suite()` target(s)][aggregation-test-suite-targets] when
adding, renaming, or removing tests.
* It is more likely that someone will remember to update `test_suite()`
target(s) in the same `BUILD.bazel` file than one (or multiple) somewhere
else in the tree.
* Test inclusion can be handled automatically in some cases by specifying an
empty `tests` attribute (see [Defining tests and test suites in individual
packages][individual-packages] above).
<!-- Reference links -->
[aggregation-test-suite-targets]: #aggregation-test-suite-targets
[bazel-test-compatibility]: /docs/development/build/bazel_concepts/tests.md#bazel-test-compatibility
[bazel-tests-guide]: /docs/development/build/bazel_concepts/tests.md
[build-files-guide]: build_files.md
[common-guide]: common.md
[connecting-to-gn]: #connecting-to-gn
[cpu-architecture]: #cpu-architecture
[fxbug-540003943]: https://fxbug.dev/540003943
[fxbug-570694755]: https://fxbug.dev/570694755
[individual-packages]: #packages
[rust-unit-tests]: #rust-unit-tests
[style-guide-landing-page]: README.md
[target-compatible-with]: build_files.md#target-compatible-with
[target-visibility]: build_files.md#target-visibility
[why-only-expose-test-suite-targets]: #why-only-expose-test-suite-targets