blob: 1f37d809fecc367cc572bfd9fcaf90d3fc0a193e [file] [edit]
# 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.
"""Utility functions for working with Bazel test targets."""
import json
import os
import sys
import typing as T
from pathlib import Path
_SCRIPT_DIR = Path(__file__).parent
sys.path.append(str(_SCRIPT_DIR))
import build_utils
from build_utils import BazelLauncher, BazelPaths
def generate_tests_json(
bazel_paths: BazelPaths,
command_runner: T.Optional[build_utils.CommandRunner] = None,
) -> tuple[list[dict[str, T.Any]], set[Path]]:
"""Generate a tests.json file corresponding to all Bazel host test targets
Args:
bazel_paths: The BazelPaths object to use for path resolution.
command_runner: An optional CommandRunner instance.
Returns:
A pair of two values which are:
- A list of dictionaries, describing each Bazel host_test() reachable
from the root_host_targets, according to the tests.json schema.
- A set of input paths, whose changes would require a regeneration of
the tests.json file.
"""
if not command_runner:
command_runner = build_utils.CommandRunner()
bazel_launcher = BazelLauncher(bazel_paths.launcher, runner=command_runner)
starlark_input = _SCRIPT_DIR / "../starlark/FuchsiaHostTestInfo.cquery"
ret = bazel_launcher.run_query(
"cquery",
[
"--config=host",
"--output=starlark",
f"--starlark:file={starlark_input}",
"deps(//build/bazel/host_tests)",
],
False,
)
if ret.returncode != 0:
raise RuntimeError(f"Failed to run bazel query: {ret.stderr}")
def make_execroot_path_relative_to_ninja_build_dir(path: str) -> str:
"""Convert a path relative to the Bazel execroot to a path relative to the Ninja build directory."""
return os.path.relpath(
bazel_paths.execroot / path, bazel_paths.ninja_build_dir
)
tests_json: list[dict[str, T.Any]] = []
for line in ret.stdout.splitlines():
line = line.strip()
if not line:
continue
# The line is a JSON-encoded object that follows the tests.json schema with
# the following exceptions:
# - The 'bazel_execroot_path' and 'bazel_execroot_runtime_deps_path' fields
# are present instead of 'path' and 'runtime_deps_path', and they contain
# paths relative to the Bazel execroot instead of the Ninja build directory.
cquery_test = json.loads(line)
# LINT.IfChange(cquery_output_schema)
label = cquery_test["label"]
cpu_map = {"x86_64": "x64", "aarch64": "arm64"}
cpu = cpu_map.get(cquery_test["cpu"], cquery_test["cpu"])
os_val = (
cquery_test["os"].capitalize() if cquery_test["os"] else "Linux"
)
test_spec = {
"environments": [
{
"dimensions": {
"os": os_val,
"cpu": cpu,
}
}
],
"expects_ssh": False,
"test": {
"name": _normalize_label(label),
"label": label,
# The source label indicates the location in the tree of the
# source code. For labels in the main workspace, ensure they
# start with "//".
"source_label": _normalize_label(label),
"path": make_execroot_path_relative_to_ninja_build_dir(
cquery_test["launcher_execroot_path"]
),
"runtime_deps": make_execroot_path_relative_to_ninja_build_dir(
cquery_test["runtime_deps_json_execroot_path"]
),
"os": cquery_test["os"],
"cpu": cquery_test["cpu"],
},
}
if cquery_test["list_cases_argument"]:
test_spec["test"]["list_cases_argument"] = cquery_test[
"list_cases_argument"
]
tests_json.append(test_spec)
# LINT.ThenChange(//build/bazel/starlark/FuchsiaHostTestInfo.cquery:cquery_output_schema)
return tests_json, {starlark_input}
def _normalize_label(label: str) -> str:
"""Return the given label in its normalized form (never starting with "@@//" or "@//")."""
for prefix in ("@@//", "@//"):
if label.startswith(prefix):
return "//" + label.removeprefix(prefix)
return label