blob: 28f3038126b90796b8482b008010c1c1ad7a5ea9 [file] [log] [blame]
# Copyright 2023 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/host.gni")
import("//build/images/tools/fastboot.gni")
import("//build/python/python_binary.gni")
import("//build/testing/host_test.gni")
import("//build/testing/host_test_data.gni")
# Declares a host-side Python E2E Mobly test.
#
# Mobly is an open-source Python test framework
# that specializes at E2E tests with complex testbeds
# such as multi-device testing.
#
# Subtargets:
# * $target_name.binary_and_config
# - The associated group target that contains metadata information on the
# test binary and test config.
#
# Example
# ```
# python_mobly_test("hello_world_test") {
# main_source = "my_mobly_test.py"
# sources = [
# "test_helpers_foo.py",
# "test_helpers_bar.py",
# ]
# libraries = [
# "//path/to/foo:lib",
# "//path/to/bar:lib",
# ]
# extra_args = [ "--a_mobly_test_flag" ]
# local_config_source = "mobly_config.yaml"
# params_source = "mobly_params.yaml"
# }
# ```
#
# Example local Mobly config YAML content
# ```
# TestBeds:
# - Name: LocalTestbed
# Controllers:
# FuchsiaDevice:
# - nodename: "fuchsia-LOCAL_NODE_NAME"
# ```
#
# Example Mobly test params YAML content
# ```
# bool_param: True
# str_param: some_string
# dict_param:
# fld_1: val_1
# fld_2: val_2
# list_param:
# - 1
# - 2
# - 3
# - 4
# ```
#
# Parameters
#
# main_source (required)
# The Mobly .py test file that will be interpreted.
# Type: path
#
# test_name (optional)
# The name of the generated test.
# Type: string
# Default: target_name
#
# sources (optional)
# Other files that are used in the Mobly test.
# Type: list(path)
# Default: empty list
#
# libraries (optional)
# Paths to python_libraries this Mobly test imports.
# Type: list(string)
# Default: empty list
#
# test_dir (optional)
# Path to where the test executes.
# Type: string
# Default: "${target_out_dir}/${target_name}"
#
# extra_args (optional)
# Additional arguments to pass to the test.
# Type: list(string)
# Default: empty list
#
# local_config_source (optional)
# Path to the Mobly YAML config file to use for local execution.
# If not provided, Mobly test will generate a best-effort config based on the
# host environment. Note that config generation assumes connected Fuchsia
# devices are provisioned using the SSH credentials from Fuchsia.git
# workflows. If provisioned via other means (e.g. SDK, Sonic), users are
# advised to provide a local config instead.
# Type: string
#
# params_source (optional)
# Path to the Mobly test params config file (typically YAML or JSON).
# If not provided, Mobly test will execute without test params.
# Type: string
#
# params (optional)
# The mobly test params defined in GN. If this is present `params_source` must not
# be present.
# Type: object
#
# transport (required)
# Transport to be used by HoneyDew while performing majority of the
# host-target interactions. Valid values are "fuchsia-controller" and "fuchsia-controller-preferred".
# Type: String
#
# ssh_path (optional)
# Path to the ssh binary to be used by Antlion connectivity testbeds for connecting
# to access points.
# Type: String
#
# test_data_deps (optional)
# List of test data GN targets that are needed at runtime.
# Type: list(string)
# Default: empty list
#
# multi_fuchsia_device (optional)
# Whether a test is a multi-device test (requires 2+ fuchsia devices).
# Type: boolean
# Default: false
#
# timeout_secs (optional)
# [int] The infra timeout in seconds for the test.
#
# environments
# Optional: what environments this test should target. Only applies to host
# host tests. See //build/testing/test_spec.gni for more details.
# Type: list of scopes.
# Default: emu_env
#
# enable_mypy (optional)
# If true, enable MyPy type checking on the target and respective deps.
# Type: boolean
# Default: true
#
# testonly (optional)
# GN Usual. Allow option to set to false for IDK inclusion.
# See https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/BUILD.gn;l=161-167;drc=aa7ba9d998556f7124fe8e2edc6604c9c1592d01
# Type: boolean
# Default: true
#
# deps
# visibility
#
# Metadata
#
# pye2e_test_sources ($target_name.binary_and_config targets only)
# List of scopes for each test-specific file including the name and path of
# the source (relative to root).
template("python_mobly_test") {
assert(defined(invoker.main_source), "main_source is required")
assert(defined(invoker.transport), "transport is required")
assert(
invoker.transport == "fuchsia-controller" ||
invoker.transport == "fuchsia-controller-preferred",
"transport should be either 'fuchsia-controller' or 'fuchsia-controller-preferred' but received: " + invoker.transport)
_testonly = true
if (defined(invoker.testonly)) {
_testonly = invoker.testonly
}
_enable_mypy = true
if (defined(invoker.enable_mypy)) {
_enable_mypy = invoker.enable_mypy
}
#
# Define Mobly test python_binary().
#
_test_name = "${target_name}"
if (defined(invoker.test_name)) {
_test_name = invoker.test_name
}
_test_binary_name = "${_test_name}.pyz"
_test_binary_target = "${_test_name}_python_binary"
python_binary(_test_binary_target) {
forward_variables_from(invoker,
[
"main_source",
"sources",
])
testonly = _testonly
enable_mypy = _enable_mypy
visibility = [ ":*" ]
output_name = _test_binary_name
# Mobly-specific entry point.
main_callable = "test_runner.main"
deps = []
if (_testonly) {
deps += [ "//third_party/mobly" ]
} else {
deps += [ "//third_party/mobly:mobly_no_testonly" ]
}
if (defined(invoker.libraries)) {
deps += invoker.libraries
}
}
_test_dir = "${target_out_dir}/${_test_name}"
if (defined(invoker.test_dir)) {
_test_dir = invoker.test_dir
}
#
# Define Mobly test host_test_data().
#
_mobly_test_data_target = "${_test_name}_test_data"
host_test_data(_mobly_test_data_target) {
visibility = [ ":*" ]
testonly = _testonly
sources = [ get_label_info(":${_test_binary_target}", "target_out_dir") +
"/${_test_binary_name}" ]
outputs = [ "${_test_dir}/${_test_binary_name}" ]
deps = [ ":${_test_binary_target}" ]
if (defined(invoker.deps)) {
deps += invoker.deps
}
}
#
# Define Fastboot binary host_test_data()
#
# This is currently required by Honeydew for Fuchsia device interactions when
# in fastboot mode. It's safe to assume that some of the python_mobly_test()
# will be performing some form of Fuchsia interactions when in fastboot mode
# so including Fastboot in this template would centralize this data deps for
# all users.
_fastboot_test_data_target = "${_test_name}_fastboot_bin_test_data"
host_test_data(_fastboot_test_data_target) {
testonly = _testonly
sources = [ "${prebuilt_fastboot}" ]
outputs = [ "${_test_dir}/fastboot" ]
}
#
# Define SSH binary host_test_data().
#
# This is currently required by Honeydew for auxiliary device (access points and
# iperf servers) interactions when running wlan affordance functional tests.
_ssh_binary_test_data_target = "${_test_name}_test_data_ssh"
host_test_data(_ssh_binary_test_data_target) {
testonly = true
visibility = [ ":*" ]
sources = [
"//prebuilt/third_party/openssh-portable/${host_os}-${host_cpu}/bin/ssh",
]
outputs = [ "${_test_dir}/ssh" ]
}
#
# Construct host test arguments.
#
# Mobly Driver is provided as the first host test argument so that it's the
# interpreter's entry-point. This allows Mobly Driver to wrap the execution
# lifecycle of the underlying Mobly test.
#
# We define Mobly Driver outside of this template so that the Mobly Driver
# binary is built only once but can be used/referenced for any Mobly tests.
_md_test_data_dir =
get_label_info("//src/testing/end_to_end/mobly_driver", "target_out_dir")
_host_test_args = [
rebase_path("${_md_test_data_dir}/test_data/mobly_driver.pyz",
root_build_dir),
rebase_path("${_test_dir}/${_test_binary_name}", root_build_dir),
"-test_data_path",
rebase_path(_test_dir, root_build_dir),
"-ffx_path",
rebase_path("${host_tools_dir}/ffx", root_build_dir),
"-transport",
invoker.transport,
"-ffx_subtools_search_path",
rebase_path(host_tools_dir, root_build_dir),
"-ssh_path",
rebase_path("${_test_dir}/ssh", root_build_dir),
]
if (defined(invoker.multi_fuchsia_device) && invoker.multi_fuchsia_device) {
_host_test_args += [ "-multi_device" ]
}
_additional_deps = []
# If specified, define test params data target and data path add to host args.
if (defined(invoker.params_source)) {
assert(!defined(invoker.params),
"Only one of params or params_source can be defined")
_params_source = invoker.params_source
_params_deps = []
} else if (defined(invoker.params)) {
_generated_params_target = "${target_name}_generated_params"
_params_source = "${target_gen_dir}/${target_name}_params.json"
generated_file(_generated_params_target) {
output_conversion = "json"
contents = invoker.params
outputs = [ _params_source ]
}
_params_deps = [ ":${_generated_params_target}" ]
}
if (defined(_params_source)) {
_param_yaml_data_target = "${_test_name}_params_data"
host_test_data(_param_yaml_data_target) {
testonly = _testonly
sources = [ _params_source ]
outputs = [ "${_test_dir}/test_params.yaml" ]
deps = _params_deps
}
_host_test_args += [
"-params_yaml_path",
rebase_path("${_test_dir}/test_params.yaml", root_build_dir),
]
_additional_deps += [ ":${_param_yaml_data_target}" ]
}
group("${target_name}.binary_and_config") {
testonly = _testonly
deps = [ ":${_test_binary_target}" ]
metadata = {
pye2e_test_sources = [
{
# Add PYZ as a test source.
name = _test_binary_name
path = rebase_path(
get_label_info(":${_test_binary_target}", "target_out_dir") +
"/${_test_binary_name}",
root_build_dir)
},
]
}
if (defined(_params_source)) {
metadata.pye2e_test_sources += [
{
name = "params.yaml"
path = rebase_path(_params_source, root_build_dir)
},
]
}
}
# If specified, define test config data target and add data path to host args.
if (defined(invoker.local_config_source)) {
_config_yaml_data_target = "${_test_name}_config_data"
host_test_data(_config_yaml_data_target) {
testonly = _testonly
sources = [ invoker.local_config_source ]
outputs = [ "${_test_dir}/config.yaml" ]
}
_host_test_args += [
"-config_yaml_path",
rebase_path("${_test_dir}/config.yaml", root_build_dir),
]
_additional_deps += [ ":${_config_yaml_data_target}" ]
}
#
# Define the Mobly host_test().
#
host_test(_test_name) {
forward_variables_from(invoker,
[
"visibility",
"timeout_secs",
])
binary_path = python_exe_src
# Disable Python's output buffering when launching Mobly Driver.
args = [ "-u" ] + _host_test_args
if (defined(invoker.extra_args)) {
args += invoker.extra_args
}
data_deps = [ "//src/developer/ffx:suite_test_data" ]
deps =
[
":${_fastboot_test_data_target}",
":${_mobly_test_data_target}",
":${_ssh_binary_test_data_target}",
"//build/python:interpreter",
"//src/testing/end_to_end/mobly_driver:mobly_driver_test_data_target",
] + _additional_deps
if (defined(invoker.test_data_deps)) {
deps += invoker.test_data_deps
}
if (defined(invoker.environments)) {
environments = invoker.environments
} else {
environments = [ emu_env ]
}
}
}