| # Copyright 2026 The Fuchsia Authors. All rights reserved. | |
| # Use of this source code is governed by a BSD-style license that can be | |
| # found in the LICENSE file. | |
| import("//build/compiled_action.gni") | |
| import("//build/components.gni") | |
| import("//build/fidl/fidl.gni") | |
| import("//build/rust/rustc_binary.gni") | |
| import("//build/rust/rustc_test.gni") | |
| import("//sdk/ctf/build/ctf.gni") | |
| # Defines a Fuchsia integration test using the TRF Codegen framework. | |
| # | |
| # Parameters | |
| # | |
| # source (required) | |
| # The path to the Rust test source file (e.g. "src/tester.rs"). | |
| # Type: path | |
| # | |
| # cut (required) | |
| # List of Component Under Test (CUT) GN target labels to include in the test realm. | |
| # Type: list(label) | |
| # | |
| # cut_component_name (optional) | |
| # Explicit name of the CUT component. If omitted, the GN label name from the first | |
| # target in the `cut` list will be used with any trailing `_component` stripped out. | |
| # Type: string | |
| # | |
| # cut_manifest (optional) | |
| # The path to the cml manifest of the CUT, used to parse exposed capability configurations. | |
| # Type: path | |
| # | |
| # deps (optional) | |
| # Additional Rust dependencies for the test driver and injectable universe binary targets. | |
| # Type: list(label) | |
| # | |
| # mocks (optional) | |
| # List of Rust source files containing mock implementations. | |
| # Type: list(path) | |
| # | |
| # is_ctf (optional) | |
| # If true, the test package will additionally be archived in the Compatibility Test Framework (CTF) | |
| # release artifacts, allowing it to be preserved across branch cuts. Defaults to false. | |
| # Type: boolean | |
| # | |
| template("trf_test") { | |
| assert(defined(invoker.source), "source must be defined for trf_test") | |
| assert(defined(invoker.cut), "cut must be defined for trf_test") | |
| _main_target_name = target_name | |
| target_out_dir_base = "${target_gen_dir}/$target_name" | |
| # Find CUT dependencies and optional manifest input | |
| _cut_manifest_inputs = [] | |
| _cut_codegen_deps = [] | |
| foreach(label, invoker.cut) { | |
| _cut_codegen_deps += [ label ] | |
| } | |
| if (defined(invoker.cut_manifest)) { | |
| _cut_manifest_inputs = [ invoker.cut_manifest ] | |
| } | |
| # 1. Action invoking trf_codegen_bin compiler | |
| codegen_target_name = "${target_name}_codegen" | |
| compiled_action(codegen_target_name) { | |
| testonly = true | |
| tool = "//src/testing/trf_codegen/bin:trf_codegen_bin" | |
| inputs = [ invoker.source ] + _cut_manifest_inputs | |
| if (defined(invoker.mocks)) { | |
| inputs += invoker.mocks | |
| } | |
| outputs = [ | |
| "$target_out_dir_base/fidl/mock_control.fidl", | |
| "$target_out_dir_base/cml/injectable_universe.cml", | |
| "$target_out_dir_base/src/injectable_universe.rs", | |
| "$target_out_dir_base/cml/realm_factory.cml", | |
| "$target_out_dir_base/src/realm_factory.rs", | |
| "$target_out_dir_base/cml/test_driver.cml", | |
| "$target_out_dir_base/cml/test_root.cml", | |
| "$target_out_dir_base/src/realm_builder_generated.rs", | |
| ] | |
| if (defined(invoker.mocks)) { | |
| _j = 0 | |
| foreach(mock_file, invoker.mocks) { | |
| _name = get_path_info(mock_file, "name") | |
| outputs += [ "$target_out_dir_base/src/mock_${_j}_${_name}.rs" ] | |
| _j += 1 | |
| } | |
| } | |
| deps = _cut_codegen_deps | |
| if (defined(invoker.cut_component_name)) { | |
| _cut_component_name = invoker.cut_component_name | |
| } else { | |
| _cut_list = invoker.cut | |
| _cut_label_name = get_label_info(_cut_list[0], "name") | |
| _cut_component_name = string_replace(_cut_label_name, "_component", "") | |
| } | |
| args = [ | |
| "--source", | |
| rebase_path(invoker.source, root_build_dir), | |
| "--out-dir", | |
| rebase_path(target_out_dir_base, root_build_dir), | |
| "--target-name", | |
| _main_target_name, | |
| "--cut-component-name", | |
| _cut_component_name, | |
| ] | |
| if (defined(invoker.mocks)) { | |
| foreach(mock_file, invoker.mocks) { | |
| args += [ | |
| "--mock", | |
| rebase_path(mock_file, root_build_dir), | |
| ] | |
| } | |
| } | |
| if (_cut_manifest_inputs != []) { | |
| args += [ | |
| "--manifest", | |
| rebase_path(_cut_manifest_inputs[0], root_build_dir), | |
| ] | |
| } | |
| } | |
| # 2. FIDL compilation targets | |
| fidl_target_name = "${target_name}_mock_control_fidl" | |
| _fidl_suffix = string_replace(target_name, "_", "") | |
| _fidl_suffix = string_replace(_fidl_suffix, "-", "") | |
| fidl(fidl_target_name) { | |
| testonly = true | |
| name = "fuchsia.trf.mockcontrol.${_fidl_suffix}" | |
| sources = [ "$target_out_dir_base/fidl/mock_control.fidl" ] | |
| non_fidl_deps = [ ":$codegen_target_name" ] | |
| } | |
| rb_generated_target_name = "${target_name}_rb_generated" | |
| rustc_library(rb_generated_target_name) { | |
| testonly = true | |
| edition = "2024" | |
| name = "trf_generated_realm" | |
| source_root = "$target_out_dir_base/src/realm_builder_generated.rs" | |
| sources = [ "$target_out_dir_base/src/realm_builder_generated.rs" ] | |
| deps = [ | |
| ":${fidl_target_name}_rust", | |
| "//src/testing/trf_codegen/fidl:fuchsia.trf.factory_rust", | |
| "//src/testing/trf_codegen/lib/runtime:trf_codegen_runtime", | |
| "//third_party/rust_crates:anyhow", | |
| ] | |
| non_rust_deps = [ ":$codegen_target_name" ] | |
| } | |
| # 3. Test Driver binary compiled from test source | |
| td_bin_target_name = "${target_name}_td_bin" | |
| rustc_test(td_bin_target_name) { | |
| edition = "2024" | |
| source_root = invoker.source | |
| sources = [ invoker.source ] | |
| deps = [ | |
| ":$codegen_target_name", | |
| ":$rb_generated_target_name", | |
| "//src/lib/fuchsia", | |
| "//src/testing/trf_codegen/lib/macro:trf_codegen_macro", | |
| "//third_party/rust_crates:anyhow", | |
| ] | |
| if (defined(invoker.deps)) { | |
| deps += invoker.deps | |
| } | |
| } | |
| # 4. Injectable Universe component, config, and binary | |
| iu_manifest_target_name = "${target_name}_iu_manifest" | |
| fuchsia_component_manifest(iu_manifest_target_name) { | |
| testonly = true | |
| component_name = "injectable_universe" | |
| manifest = "$target_out_dir_base/cml/injectable_universe.cml" | |
| deps = [ ":$codegen_target_name" ] | |
| } | |
| iu_config_target_name = "${target_name}_iu_config" | |
| fuchsia_structured_config_rust_lib(iu_config_target_name) { | |
| testonly = true | |
| name = "injectable_universe_config" | |
| cm_label = ":$iu_manifest_target_name" | |
| } | |
| iu_cvf_target_name = "${target_name}_iu_cvf" | |
| fuchsia_structured_config_values(iu_cvf_target_name) { | |
| testonly = true | |
| cm_label = ":$iu_manifest_target_name" | |
| values = { | |
| # The injectable universe dynamically expects a 'hat' parameter via | |
| # structured config which serves as a namespace partition string routing | |
| # concurrent mock data, but it defaults to an empty global namespace. | |
| hat = "" | |
| } | |
| } | |
| iu_bin_target_name = "${target_name}_iu_bin" | |
| rustc_binary(iu_bin_target_name) { | |
| testonly = true | |
| edition = "2024" | |
| source_root = "$target_out_dir_base/src/injectable_universe.rs" | |
| sources = [ "$target_out_dir_base/src/injectable_universe.rs" ] | |
| if (defined(invoker.mocks)) { | |
| _i = 0 | |
| foreach(mock, invoker.mocks) { | |
| _name = get_path_info(mock, "name") | |
| sources += [ "$target_out_dir_base/src/mock_${_i}_${_name}.rs" ] | |
| _i += 1 | |
| } | |
| } | |
| deps = [ | |
| ":$iu_config_target_name", | |
| ":${fidl_target_name}_rust", | |
| "//src/lib/fuchsia", | |
| "//src/lib/fuchsia-async", | |
| "//src/lib/fuchsia-component", | |
| "//src/testing/trf_codegen/lib/macro:trf_codegen_macro", | |
| "//third_party/rust_crates:anyhow", | |
| "//third_party/rust_crates:futures", | |
| "//third_party/rust_crates:tracing", | |
| ] | |
| if (defined(invoker.deps)) { | |
| deps += invoker.deps | |
| } | |
| non_rust_deps = [ ":$codegen_target_name" ] | |
| } | |
| iu_comp_target_name = "${target_name}_injectable_universe" | |
| fuchsia_component(iu_comp_target_name) { | |
| testonly = true | |
| cm_label = ":$iu_manifest_target_name" | |
| deps = [ ":$iu_bin_target_name" ] | |
| } | |
| iu_pkg_target_name = "${iu_comp_target_name}_pkg" | |
| fuchsia_package(iu_pkg_target_name) { | |
| package_name = "${_main_target_name}_injectable_universe" | |
| testonly = true | |
| validate_structured_config = false | |
| deps = [ | |
| ":$iu_comp_target_name", | |
| ":$iu_cvf_target_name", | |
| ] | |
| } | |
| # 5. Test Driver component | |
| td_comp_target_name = "${target_name}_test_driver" | |
| fuchsia_component(td_comp_target_name) { | |
| testonly = true | |
| component_name = "test_driver" | |
| manifest = "$target_out_dir_base/cml/test_driver.cml" | |
| deps = [ | |
| ":$codegen_target_name", | |
| ":$td_bin_target_name", | |
| ] | |
| } | |
| # 6. Realm Factory component & package | |
| rf_bin_target_name = "${target_name}_rf_bin" | |
| rustc_binary(rf_bin_target_name) { | |
| testonly = true | |
| edition = "2024" | |
| source_root = "$target_out_dir_base/src/realm_factory.rs" | |
| sources = [ "$target_out_dir_base/src/realm_factory.rs" ] | |
| deps = [ | |
| "//sdk/fidl/fuchsia.component.decl:fuchsia.component.decl_rust", | |
| "//sdk/fidl/fuchsia.testing.harness:fuchsia.testing.harness_rust", | |
| "//sdk/rust/zx", | |
| "//src/lib/fidl/rust/fidl", | |
| "//src/lib/fuchsia", | |
| "//src/lib/fuchsia-async", | |
| "//src/lib/fuchsia-component", | |
| "//src/lib/fuchsia-component-test", | |
| "//src/sys/lib/cm_rust", | |
| "//src/testing/realm_proxy", | |
| "//src/testing/trf_codegen/fidl:fuchsia.trf.factory_rust", | |
| "//third_party/rust_crates:anyhow", | |
| "//third_party/rust_crates:futures", | |
| "//third_party/rust_crates:tracing", | |
| ] | |
| non_rust_deps = [ ":$codegen_target_name" ] | |
| } | |
| rf_comp_target_name = "${target_name}_realm_factory_comp" | |
| fuchsia_component(rf_comp_target_name) { | |
| testonly = true | |
| component_name = "realm_factory" | |
| manifest = "$target_out_dir_base/cml/realm_factory.cml" | |
| deps = [ | |
| ":$codegen_target_name", | |
| ":$rf_bin_target_name", | |
| ] | |
| } | |
| rf_pkg_target_name = "${target_name}_realm_factory_pkg" | |
| fuchsia_package(rf_pkg_target_name) { | |
| package_name = "${_main_target_name}_realm_factory" | |
| testonly = true | |
| deps = [ | |
| ":$rf_comp_target_name", | |
| "//src/lib/fuchsia-component-test/realm_builder_server:realm_builder_server_component", | |
| ] | |
| subpackages = invoker.cut + [ ":${iu_pkg_target_name}" ] | |
| } | |
| # 7. Test Root component | |
| test_root_comp_target_name = "${target_name}_test_root" | |
| fuchsia_test_component(test_root_comp_target_name) { | |
| testonly = true | |
| component_name = "test_root" | |
| test_type = "system" | |
| manifest = "$target_out_dir_base/cml/test_root.cml" | |
| deps = [ ":$codegen_target_name" ] | |
| } | |
| # 8. Main Test Package | |
| fuchsia_test_package(target_name) { | |
| validate_structured_config = false | |
| test_components = [ ":$test_root_comp_target_name" ] | |
| deps = [ ":$td_comp_target_name" ] | |
| subpackages = [ ":$rf_pkg_target_name" ] | |
| } | |
| _is_ctf = defined(invoker.is_ctf) && invoker.is_ctf | |
| if (_is_ctf) { | |
| main_target_name = target_name | |
| ctf_fuchsia_package_archive("${target_name}_ctf_archive") { | |
| package = ":$main_target_name" | |
| package_name = main_target_name | |
| test_component_names = [ "test_root" ] | |
| } | |
| } else { | |
| group("${target_name}_ctf_archive") { | |
| } | |
| } | |
| } |