本文档对我们在 GN 构建系统中定义和组织 FIDL 测试的方式进行了标准化,并遵循以下目标:
- 命名保持一致。如果 Rust 使用
fx test fidl_rust_conformance_tests,则 HLCPP 应使用fx test fidl_hlcpp_conformance_tests。一致且可预测的命名可提供更好的开发者体验。 - 运行所需内容。测试工作流应能轻松运行单个测试组件,而无需构建或运行任何额外内容。
- 在主机上运行。测试应尽可能支持在主机(非 Fuchsia)上运行,因为主机上的“修改-构建-运行”周期通常要快得多。
- 遵循最佳实践。我们应遵循 Fuchsia 在使用
fx test、构建组件等方面的最佳实践。
术语
本文档使用以下术语:
- target:在 BUILD.gn 文件中定义的 GN 目标
- toolchain:请参阅
fx gn help toolchain - 主机:开发者的平台,特别是 Linux
- device:Fuchsia 平台,可以是实体设备,也可以是模拟设备(即 qemu)
- package:Fuchsia 软件包;Fuchsia 中的分发单元
- 组件:Fuchsia 组件;Fuchsia 中的可执行软件单元
命名
通用准则:
- 使用下划线,而不是连字符。
- 名称应以复数
_tests结尾,而不是单数_test。 - 为软件包、组件和二进制文件使用完整、描述性、唯一的名称。
最后一点意味着,应优先使用全名(如 fidl_rust_conformance_tests),而不是上下文名称(如 conformance_tests)。在目录、软件包、组件和二进制文件级别重复“fidl”和“rust”可能看起来冗长且多余。但事实上,这些名称必须是唯一的,最好以一致的方式使它们具有唯一性,而不是记住诸如 fidl-bindings-test 用于 Dart、fidl-test 用于 C 等奇怪的规则。
名称应采用以下方案,并使用下划线连接各部分:
tool [ bindings ] [ category [ subcategory ] ] tests
其中,tool 是以下项之一:
- fidl:FIDL 运行时支持
- fidlc:FIDL 编译器前端
- fidlgen:FIDL 编译器后端
- gidl、measure_tape 等:其他工具
其他部分包括:
- bindings
- c、cpp、cpp_wire、hlcpp、rust、dart 之一
- category、subcategory
- 示例类别:conformance、types、parser、lib
- 请勿使用:frontend、backend、bindings(工具会区分这些内容)
层次结构
每个定义测试的 BUILD.gn 文件都应包含一个 "tests" 组:
group("tests") {
testonly = true
deps = [ ... ] # not public_deps
}
如果目录以“tests”结尾,并且 BUILD.gn 文件仅定义测试目标,则该组应与目录名称匹配。例如,foo_tests/BUILD.gn 可以使用 group("foo_tests")。这样可以启用 GN 标签简写 //path/to/foo_tests,等同于 //path/to/foo_tests:foo_tests。
这些组会汇总到父级目录中 BUILD.gn 文件的“tests”组中。根“tests”组(针对代码库的某些部分,例如 src/lib/fidl/BUILD.gn)应包含在 bundles/fidl/BUILD.gn 中。这样,fx set ... --with //bundles/fidl:tests 就可以在 build 中包含所有 FIDL 测试。
(由于 //bundles/buildbot/core 包含 //bundles/fidl:tests,因此这些测试也会在 CQ 中运行。)
二进制文件名称
通常,测试二进制文件名称基于目标名称。例如,test("some_tests") { ... } 目标会生成 some_tests 二进制文件。不过,对于单个测试,您通常需要多个具有唯一名称的目标(源代码集、组件、软件包等)。因此,本文档中的示例使用 some_tests_bin 等目标名称,并使用 output_name 参数替换二进制文件名称:
test("some_tests_bin") {
output_name = "some_tests"
...
}
这也适用于 rustc_test、go_test 等。
设备测试
假设我们有一个生成 fidl_foo_tests 二进制文件的 :fidl_foo_tests_bin 目标。如需将其封装在软件包中,请使用 fuchsia_unittest_package:
import("//build/components.gni")
fuchsia_unittest_package("fidl_foo_tests") {
deps = [ ":fidl_foo_tests_bin" ]
}
现在,我们可以使用 fx test fidl_foo_tests 按软件包名称或组件名称(两者相同)运行测试。
为每个测试使用单独的软件包。如果将不相关的测试组件捆绑在一个软件包中,运行其中一个测试会导致整个软件包重新构建。只有在多个测试组件需要一起测试时,才应将它们打包在一起,例如客户端和服务器集成测试。如需查看示例,请参阅复杂拓扑和集成测试。
如果您的测试需要 fuchsia_unittest_component 默认设置之外的任何组件功能、服务等,您必须编写组件清单文件:
# 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"
]
}
]
}
如需详细了解软件包和组件模板,请参阅构建组件。
主机测试
假设我们有一个生成 fidl_bar_tests 二进制文件的 :fidl_bar_tests_bin 目标。我们必须确保 GN 在达到该目标时处于 $host_toolchain 中,否则它会尝试为 Fuchsia 构建该目标:
groups("tests") {
testonly = true
deps = [ ":fidl_bar_tests_bin($host_toolchain)" ]
}
(始终将 ($host_toolchain) 放在 BUILD.gn 文件的 tests 组中,而不是 //bundles/fidl:tests 中。)
这将创建一个名为 host_x64/fidl_bar_tests 的 test_spec 条目,该条目最终会出现在 out/default/tests.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"
}
由于 tests.json 中有“name”字段,因此运行 fx test fidl_bar_tests 是可行的。
主机/设备测试
在主机和设备上运行的测试分为两类。在第一类中,测试目标只需在任一工具链下构建。例如:
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",
]
}
现在,我们可以通过以下两种方式运行测试:
- 在设备上:
fx test fidl_rust_conformance_tests --device - 主机:
fx test fidl_rust_conformance_tests --host
在第二类中,设备测试和主机测试共享源代码,但它们有很大不同,因此必须由单独的目标定义。这需要将主机测试定义封装在 if (is_host) { ... } 中,以防止 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",
]
}
现在,我们可以通过以下两种方式运行测试:
- 在设备上:
fx test fidl_hlcpp_conformance_tests --device - 主机:
fx test fidl_hlcpp_conformance_tests --host
Rust 单元测试
Rust 库可以按如下方式定义:
rustc_library("baz") {
with_unit_tests = true
...
}
这会自动创建一个 baz_test 目标,用于构建 baz_lib_test 二进制文件。请勿使用此方法,原因有两点:
请改为编写一个具有适当名称的单独 rustc_test 目标,而不是 with_unit_tests:
rustc_library("baz") {
...
}
rustc_test("fidl_baz_tests") {
...
}
分组
假设我们有以下测试结构:
- FIDL Rust
- 设备
- 一致性
- 集成
- 主机
- 一致性
- 设备
我们应该为叶节点设置测试目标:
fx test fidl_rust_conformance_testsfx test fidl_rust_integration_tests
我们不应为运行各种测试子集而创建额外的软件包。使用fx test,我们已经可以
- 运行所有测试:
fx test //path/to/fidl/rust - 运行所有设备测试:
fx test //path/to/fidl/rust --device - 运行所有主机测试:
fx test //path/to/fidl/rust --host