xref: /aosp_15_r20/external/crosvm/tools/run_tests (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*bb4ee6a4SAndroid Build Coastguard Worker# Copyright 2023 The ChromiumOS Authors
3*bb4ee6a4SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
4*bb4ee6a4SAndroid Build Coastguard Worker# found in the LICENSE file.
5*bb4ee6a4SAndroid Build Coastguard Worker
6*bb4ee6a4SAndroid Build Coastguard Workerimport copy
7*bb4ee6a4SAndroid Build Coastguard Workerimport os
8*bb4ee6a4SAndroid Build Coastguard Workerfrom pathlib import Path
9*bb4ee6a4SAndroid Build Coastguard Workerimport sys
10*bb4ee6a4SAndroid Build Coastguard Workerfrom typing import Any, Iterable, List, Optional, Union
11*bb4ee6a4SAndroid Build Coastguard Workerfrom impl.common import (
12*bb4ee6a4SAndroid Build Coastguard Worker    CROSVM_ROOT,
13*bb4ee6a4SAndroid Build Coastguard Worker    TOOLS_ROOT,
14*bb4ee6a4SAndroid Build Coastguard Worker    Command,
15*bb4ee6a4SAndroid Build Coastguard Worker    Remote,
16*bb4ee6a4SAndroid Build Coastguard Worker    quoted,
17*bb4ee6a4SAndroid Build Coastguard Worker    Styles,
18*bb4ee6a4SAndroid Build Coastguard Worker    argh,
19*bb4ee6a4SAndroid Build Coastguard Worker    console,
20*bb4ee6a4SAndroid Build Coastguard Worker    chdir,
21*bb4ee6a4SAndroid Build Coastguard Worker    cmd,
22*bb4ee6a4SAndroid Build Coastguard Worker    record_time,
23*bb4ee6a4SAndroid Build Coastguard Worker    run_main,
24*bb4ee6a4SAndroid Build Coastguard Worker    sudo_is_passwordless,
25*bb4ee6a4SAndroid Build Coastguard Worker    verbose,
26*bb4ee6a4SAndroid Build Coastguard Worker    Triple,
27*bb4ee6a4SAndroid Build Coastguard Worker)
28*bb4ee6a4SAndroid Build Coastguard Workerfrom impl.test_config import ROOT_TESTS, DO_NOT_RUN, DO_NOT_RUN_AARCH64, DO_NOT_RUN_WIN64, E2E_TESTS
29*bb4ee6a4SAndroid Build Coastguard Workerfrom impl.test_config import DO_NOT_BUILD_RISCV64, DO_NOT_RUN_WINE64
30*bb4ee6a4SAndroid Build Coastguard Workerfrom impl import testvm
31*bb4ee6a4SAndroid Build Coastguard Worker
32*bb4ee6a4SAndroid Build Coastguard Workerrsync = cmd("rsync")
33*bb4ee6a4SAndroid Build Coastguard Workercargo = cmd("cargo")
34*bb4ee6a4SAndroid Build Coastguard Worker
35*bb4ee6a4SAndroid Build Coastguard Worker# Name of the directory used to package all test files.
36*bb4ee6a4SAndroid Build Coastguard WorkerPACKAGE_NAME = "integration_tests_package"
37*bb4ee6a4SAndroid Build Coastguard Worker
38*bb4ee6a4SAndroid Build Coastguard Worker
39*bb4ee6a4SAndroid Build Coastguard Workerdef join_filters(items: Iterable[str], op: str):
40*bb4ee6a4SAndroid Build Coastguard Worker    return op.join(f"({i})" for i in items)
41*bb4ee6a4SAndroid Build Coastguard Worker
42*bb4ee6a4SAndroid Build Coastguard Worker
43*bb4ee6a4SAndroid Build Coastguard Workerclass TestFilter(object):
44*bb4ee6a4SAndroid Build Coastguard Worker    """
45*bb4ee6a4SAndroid Build Coastguard Worker    Utility structure to join user-provided filter expressions with additional filters
46*bb4ee6a4SAndroid Build Coastguard Worker
47*bb4ee6a4SAndroid Build Coastguard Worker    See https://nexte.st/book/filter-expressions.html
48*bb4ee6a4SAndroid Build Coastguard Worker    """
49*bb4ee6a4SAndroid Build Coastguard Worker
50*bb4ee6a4SAndroid Build Coastguard Worker    def __init__(self, expression: str):
51*bb4ee6a4SAndroid Build Coastguard Worker        self.expression = expression
52*bb4ee6a4SAndroid Build Coastguard Worker
53*bb4ee6a4SAndroid Build Coastguard Worker    def exclude(self, *exclude_exprs: str):
54*bb4ee6a4SAndroid Build Coastguard Worker        return self.subset(f"not ({join_filters(exclude_exprs, '|')})")
55*bb4ee6a4SAndroid Build Coastguard Worker
56*bb4ee6a4SAndroid Build Coastguard Worker    def include(self, *include_exprs: str):
57*bb4ee6a4SAndroid Build Coastguard Worker        include_expr = join_filters(include_exprs, "|")
58*bb4ee6a4SAndroid Build Coastguard Worker        return TestFilter(f"({self.expression}) | ({include_expr})")
59*bb4ee6a4SAndroid Build Coastguard Worker
60*bb4ee6a4SAndroid Build Coastguard Worker    def subset(self, *subset_exprs: str):
61*bb4ee6a4SAndroid Build Coastguard Worker        subset_expr = join_filters(subset_exprs, "|")
62*bb4ee6a4SAndroid Build Coastguard Worker        if not self.expression:
63*bb4ee6a4SAndroid Build Coastguard Worker            return TestFilter(subset_expr)
64*bb4ee6a4SAndroid Build Coastguard Worker        return TestFilter(f"({self.expression}) & ({subset_expr})")
65*bb4ee6a4SAndroid Build Coastguard Worker
66*bb4ee6a4SAndroid Build Coastguard Worker    def to_args(self):
67*bb4ee6a4SAndroid Build Coastguard Worker        if not self.expression:
68*bb4ee6a4SAndroid Build Coastguard Worker            return
69*bb4ee6a4SAndroid Build Coastguard Worker        yield "--filter-expr"
70*bb4ee6a4SAndroid Build Coastguard Worker        yield quoted(self.expression)
71*bb4ee6a4SAndroid Build Coastguard Worker
72*bb4ee6a4SAndroid Build Coastguard Worker
73*bb4ee6a4SAndroid Build Coastguard Workerdef configure_cargo(
74*bb4ee6a4SAndroid Build Coastguard Worker    cmd: Command, triple: Triple, features: Optional[str], no_default_features: bool
75*bb4ee6a4SAndroid Build Coastguard Worker):
76*bb4ee6a4SAndroid Build Coastguard Worker    "Configures the provided cmd with cargo arguments and environment needed to build for triple."
77*bb4ee6a4SAndroid Build Coastguard Worker    return (
78*bb4ee6a4SAndroid Build Coastguard Worker        cmd.with_args(
79*bb4ee6a4SAndroid Build Coastguard Worker            "--workspace",
80*bb4ee6a4SAndroid Build Coastguard Worker            "--no-default-features" if no_default_features else None,
81*bb4ee6a4SAndroid Build Coastguard Worker            f"--features={features}" if features else None,
82*bb4ee6a4SAndroid Build Coastguard Worker        )
83*bb4ee6a4SAndroid Build Coastguard Worker        .with_color_flag()
84*bb4ee6a4SAndroid Build Coastguard Worker        .with_envs(triple.get_cargo_env())
85*bb4ee6a4SAndroid Build Coastguard Worker    )
86*bb4ee6a4SAndroid Build Coastguard Worker
87*bb4ee6a4SAndroid Build Coastguard Worker
88*bb4ee6a4SAndroid Build Coastguard Workerclass HostTarget(object):
89*bb4ee6a4SAndroid Build Coastguard Worker    def __init__(self, package_dir: Path):
90*bb4ee6a4SAndroid Build Coastguard Worker        self.run_cmd = cmd(package_dir / "run.sh").with_color_flag()
91*bb4ee6a4SAndroid Build Coastguard Worker
92*bb4ee6a4SAndroid Build Coastguard Worker    def run_tests(self, extra_args: List[Any]):
93*bb4ee6a4SAndroid Build Coastguard Worker        return self.run_cmd.with_args(*extra_args).fg(style=Styles.live_truncated(), check=False)
94*bb4ee6a4SAndroid Build Coastguard Worker
95*bb4ee6a4SAndroid Build Coastguard Worker
96*bb4ee6a4SAndroid Build Coastguard Workerclass SshTarget(object):
97*bb4ee6a4SAndroid Build Coastguard Worker    def __init__(self, package_archive: Path, remote: Remote):
98*bb4ee6a4SAndroid Build Coastguard Worker        console.print("Transfering integration tests package...")
99*bb4ee6a4SAndroid Build Coastguard Worker        with record_time("Transfering"):
100*bb4ee6a4SAndroid Build Coastguard Worker            remote.scp([package_archive], "")
101*bb4ee6a4SAndroid Build Coastguard Worker        with record_time("Unpacking"):
102*bb4ee6a4SAndroid Build Coastguard Worker            remote.ssh(cmd("tar xaf", package_archive.name)).fg(style=Styles.live_truncated())
103*bb4ee6a4SAndroid Build Coastguard Worker        self.remote_run_cmd = cmd(f"{PACKAGE_NAME}/run.sh").with_color_flag()
104*bb4ee6a4SAndroid Build Coastguard Worker        self.remote = remote
105*bb4ee6a4SAndroid Build Coastguard Worker
106*bb4ee6a4SAndroid Build Coastguard Worker    def run_tests(self, extra_args: List[Any]):
107*bb4ee6a4SAndroid Build Coastguard Worker        return self.remote.ssh(self.remote_run_cmd.with_args(*extra_args)).fg(
108*bb4ee6a4SAndroid Build Coastguard Worker            style=Styles.live_truncated(),
109*bb4ee6a4SAndroid Build Coastguard Worker            check=False,
110*bb4ee6a4SAndroid Build Coastguard Worker        )
111*bb4ee6a4SAndroid Build Coastguard Worker
112*bb4ee6a4SAndroid Build Coastguard Worker
113*bb4ee6a4SAndroid Build Coastguard Workerdef check_host_prerequisites(run_root_tests: bool):
114*bb4ee6a4SAndroid Build Coastguard Worker    "Check various prerequisites for executing test binaries."
115*bb4ee6a4SAndroid Build Coastguard Worker    if os.name == "nt":
116*bb4ee6a4SAndroid Build Coastguard Worker        return
117*bb4ee6a4SAndroid Build Coastguard Worker
118*bb4ee6a4SAndroid Build Coastguard Worker    if run_root_tests:
119*bb4ee6a4SAndroid Build Coastguard Worker        console.print("Running tests that require root privileges. Refreshing sudo now.")
120*bb4ee6a4SAndroid Build Coastguard Worker        cmd("sudo true").fg()
121*bb4ee6a4SAndroid Build Coastguard Worker
122*bb4ee6a4SAndroid Build Coastguard Worker    for device in ["/dev/kvm", "/dev/vhost-vsock"]:
123*bb4ee6a4SAndroid Build Coastguard Worker        if not os.access(device, os.R_OK | os.W_OK):
124*bb4ee6a4SAndroid Build Coastguard Worker            console.print(f"{device} access is required", style="red")
125*bb4ee6a4SAndroid Build Coastguard Worker            sys.exit(1)
126*bb4ee6a4SAndroid Build Coastguard Worker
127*bb4ee6a4SAndroid Build Coastguard Worker
128*bb4ee6a4SAndroid Build Coastguard Workerdef check_build_prerequisites(triple: Triple):
129*bb4ee6a4SAndroid Build Coastguard Worker    installed_toolchains = cmd("rustup target list --installed").lines()
130*bb4ee6a4SAndroid Build Coastguard Worker    if str(triple) not in installed_toolchains:
131*bb4ee6a4SAndroid Build Coastguard Worker        console.print(f"Your host is not configured to build for [green]{triple}[/green]")
132*bb4ee6a4SAndroid Build Coastguard Worker        console.print(f"[green]Tip:[/green] Run tests in the dev container with:")
133*bb4ee6a4SAndroid Build Coastguard Worker        console.print()
134*bb4ee6a4SAndroid Build Coastguard Worker        console.print(
135*bb4ee6a4SAndroid Build Coastguard Worker            f"  [blue]$ tools/dev_container tools/run_tests {' '.join(sys.argv[1:])}[/blue]"
136*bb4ee6a4SAndroid Build Coastguard Worker        )
137*bb4ee6a4SAndroid Build Coastguard Worker        sys.exit(1)
138*bb4ee6a4SAndroid Build Coastguard Worker
139*bb4ee6a4SAndroid Build Coastguard Worker
140*bb4ee6a4SAndroid Build Coastguard Workerdef get_vm_arch(triple: Triple):
141*bb4ee6a4SAndroid Build Coastguard Worker    if str(triple) == "x86_64-unknown-linux-gnu":
142*bb4ee6a4SAndroid Build Coastguard Worker        return "x86_64"
143*bb4ee6a4SAndroid Build Coastguard Worker    elif str(triple) == "aarch64-unknown-linux-gnu":
144*bb4ee6a4SAndroid Build Coastguard Worker        return "aarch64"
145*bb4ee6a4SAndroid Build Coastguard Worker    elif str(triple) == "riscv64gc-unknown-linux-gnu":
146*bb4ee6a4SAndroid Build Coastguard Worker        return "riscv64"
147*bb4ee6a4SAndroid Build Coastguard Worker    else:
148*bb4ee6a4SAndroid Build Coastguard Worker        raise Exception(f"{triple} is not supported for running tests in a VM.")
149*bb4ee6a4SAndroid Build Coastguard Worker
150*bb4ee6a4SAndroid Build Coastguard Worker
151*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg("--filter-expr", "-E", type=str, action="append", help="Nextest filter expression.")
152*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg(
153*bb4ee6a4SAndroid Build Coastguard Worker    "--platform", "-p", help="Which platform to test. (x86_64, aarch64, armhw, mingw64, riscv64)"
154*bb4ee6a4SAndroid Build Coastguard Worker)
155*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg("--dut", help="Which device to test on. (vm or host)")
156*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg("--no-default-features", help="Don't enable default features")
157*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg("--no-run", "--build-only", help="Build only, do not run any tests.")
158*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg("--no-unit-tests", help="Do not run unit tests.")
159*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg("--no-integration-tests", help="Do not run integration tests.")
160*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg("--no-strip", help="Do not strip test binaries of debug info.")
161*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg("--run-root-tests", help="Enables integration tests that require root privileges.")
162*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg(
163*bb4ee6a4SAndroid Build Coastguard Worker    "--features",
164*bb4ee6a4SAndroid Build Coastguard Worker    help=f"List of comma separated features to be passed to cargo. Defaults to `all-$platform`",
165*bb4ee6a4SAndroid Build Coastguard Worker)
166*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg("--no-parallel", help="Do not parallelize integration tests. Slower but more stable.")
167*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg("--repetitions", help="Repeat all tests, useful for checking test stability.")
168*bb4ee6a4SAndroid Build Coastguard Workerdef main(
169*bb4ee6a4SAndroid Build Coastguard Worker    filter_expr: List[str] = [],
170*bb4ee6a4SAndroid Build Coastguard Worker    platform: Optional[str] = None,
171*bb4ee6a4SAndroid Build Coastguard Worker    dut: Optional[str] = None,
172*bb4ee6a4SAndroid Build Coastguard Worker    no_default_features: bool = False,
173*bb4ee6a4SAndroid Build Coastguard Worker    no_run: bool = False,
174*bb4ee6a4SAndroid Build Coastguard Worker    no_unit_tests: bool = False,
175*bb4ee6a4SAndroid Build Coastguard Worker    no_integration_tests: bool = False,
176*bb4ee6a4SAndroid Build Coastguard Worker    no_strip: bool = False,
177*bb4ee6a4SAndroid Build Coastguard Worker    run_root_tests: bool = False,
178*bb4ee6a4SAndroid Build Coastguard Worker    features: Optional[str] = None,
179*bb4ee6a4SAndroid Build Coastguard Worker    no_parallel: bool = False,
180*bb4ee6a4SAndroid Build Coastguard Worker    repetitions: int = 1,
181*bb4ee6a4SAndroid Build Coastguard Worker):
182*bb4ee6a4SAndroid Build Coastguard Worker    """
183*bb4ee6a4SAndroid Build Coastguard Worker    Runs all crosvm tests
184*bb4ee6a4SAndroid Build Coastguard Worker
185*bb4ee6a4SAndroid Build Coastguard Worker    For details on how crosvm tests are organized, see https://crosvm.dev/book/testing/index.html
186*bb4ee6a4SAndroid Build Coastguard Worker
187*bb4ee6a4SAndroid Build Coastguard Worker    # Basic Usage
188*bb4ee6a4SAndroid Build Coastguard Worker
189*bb4ee6a4SAndroid Build Coastguard Worker    To run all unit tests for the hosts native architecture:
190*bb4ee6a4SAndroid Build Coastguard Worker
191*bb4ee6a4SAndroid Build Coastguard Worker    $ ./tools/run_tests
192*bb4ee6a4SAndroid Build Coastguard Worker
193*bb4ee6a4SAndroid Build Coastguard Worker    To run all unit tests for another supported architecture using an emulator (e.g. wine64,
194*bb4ee6a4SAndroid Build Coastguard Worker    qemu user space emulation).
195*bb4ee6a4SAndroid Build Coastguard Worker
196*bb4ee6a4SAndroid Build Coastguard Worker    $ ./tools/run_tests -p aarch64
197*bb4ee6a4SAndroid Build Coastguard Worker    $ ./tools/run_tests -p armhw
198*bb4ee6a4SAndroid Build Coastguard Worker    $ ./tools/run_tests -p mingw64
199*bb4ee6a4SAndroid Build Coastguard Worker
200*bb4ee6a4SAndroid Build Coastguard Worker    # Integration Tests
201*bb4ee6a4SAndroid Build Coastguard Worker
202*bb4ee6a4SAndroid Build Coastguard Worker    Integration tests can be run on a built-in virtual machine:
203*bb4ee6a4SAndroid Build Coastguard Worker
204*bb4ee6a4SAndroid Build Coastguard Worker    $ ./tools/run_tests --dut=vm
205*bb4ee6a4SAndroid Build Coastguard Worker    $ ./tools/run_tests --dut=vm -p aarch64
206*bb4ee6a4SAndroid Build Coastguard Worker
207*bb4ee6a4SAndroid Build Coastguard Worker    The virtual machine is automatically started for the test process and can be managed via the
208*bb4ee6a4SAndroid Build Coastguard Worker    `./tools/x86vm` or `./tools/aarch64vm` tools.
209*bb4ee6a4SAndroid Build Coastguard Worker
210*bb4ee6a4SAndroid Build Coastguard Worker    Integration tests can be run on the host machine as well, but cannot be guaranteed to work on
211*bb4ee6a4SAndroid Build Coastguard Worker    all configurations.
212*bb4ee6a4SAndroid Build Coastguard Worker
213*bb4ee6a4SAndroid Build Coastguard Worker    $ ./tools/run_tests --dut=host
214*bb4ee6a4SAndroid Build Coastguard Worker
215*bb4ee6a4SAndroid Build Coastguard Worker    # Test Filtering
216*bb4ee6a4SAndroid Build Coastguard Worker
217*bb4ee6a4SAndroid Build Coastguard Worker    This script supports nextest filter expressions: https://nexte.st/book/filter-expressions.html
218*bb4ee6a4SAndroid Build Coastguard Worker
219*bb4ee6a4SAndroid Build Coastguard Worker    For example to run all tests in `my-crate` and all crates that depend on it:
220*bb4ee6a4SAndroid Build Coastguard Worker
221*bb4ee6a4SAndroid Build Coastguard Worker    $ ./tools/run_tests [--dut=] -E 'rdeps(my-crate)'
222*bb4ee6a4SAndroid Build Coastguard Worker    """
223*bb4ee6a4SAndroid Build Coastguard Worker    chdir(CROSVM_ROOT)
224*bb4ee6a4SAndroid Build Coastguard Worker
225*bb4ee6a4SAndroid Build Coastguard Worker    if os.name == "posix" and not cmd("which cargo-nextest").success():
226*bb4ee6a4SAndroid Build Coastguard Worker        raise Exception("Cannot find cargo-nextest. Please re-run `./tools/install-deps`")
227*bb4ee6a4SAndroid Build Coastguard Worker    elif os.name == "nt" and not cmd("where.exe cargo-nextest.exe").success():
228*bb4ee6a4SAndroid Build Coastguard Worker        raise Exception("Cannot find cargo-nextest. Please re-run `./tools/install-deps.ps1`")
229*bb4ee6a4SAndroid Build Coastguard Worker
230*bb4ee6a4SAndroid Build Coastguard Worker    triple = Triple.from_shorthand(platform) if platform else Triple.host_default()
231*bb4ee6a4SAndroid Build Coastguard Worker
232*bb4ee6a4SAndroid Build Coastguard Worker    test_filter = TestFilter(join_filters(filter_expr, "|"))
233*bb4ee6a4SAndroid Build Coastguard Worker
234*bb4ee6a4SAndroid Build Coastguard Worker    if not features and not no_default_features:
235*bb4ee6a4SAndroid Build Coastguard Worker        features = triple.feature_flag
236*bb4ee6a4SAndroid Build Coastguard Worker
237*bb4ee6a4SAndroid Build Coastguard Worker    if no_run:
238*bb4ee6a4SAndroid Build Coastguard Worker        no_integration_tests = True
239*bb4ee6a4SAndroid Build Coastguard Worker        no_unit_tests = True
240*bb4ee6a4SAndroid Build Coastguard Worker
241*bb4ee6a4SAndroid Build Coastguard Worker    # Disable the DUT if integration tests are not run.
242*bb4ee6a4SAndroid Build Coastguard Worker    if no_integration_tests:
243*bb4ee6a4SAndroid Build Coastguard Worker        dut = None
244*bb4ee6a4SAndroid Build Coastguard Worker
245*bb4ee6a4SAndroid Build Coastguard Worker    # Automatically enable tests that require root if sudo is passwordless
246*bb4ee6a4SAndroid Build Coastguard Worker    if not run_root_tests:
247*bb4ee6a4SAndroid Build Coastguard Worker        if dut == "host":
248*bb4ee6a4SAndroid Build Coastguard Worker            run_root_tests = sudo_is_passwordless()
249*bb4ee6a4SAndroid Build Coastguard Worker        elif dut == "vm":
250*bb4ee6a4SAndroid Build Coastguard Worker            # The test VMs have passwordless sudo configured.
251*bb4ee6a4SAndroid Build Coastguard Worker            run_root_tests = True
252*bb4ee6a4SAndroid Build Coastguard Worker
253*bb4ee6a4SAndroid Build Coastguard Worker    # Print summary of tests and where they will be executed.
254*bb4ee6a4SAndroid Build Coastguard Worker    if dut == "host":
255*bb4ee6a4SAndroid Build Coastguard Worker        dut_str = "Run on host"
256*bb4ee6a4SAndroid Build Coastguard Worker    elif dut == "vm" and os.name == "posix":
257*bb4ee6a4SAndroid Build Coastguard Worker        dut_str = f"Run on built-in {get_vm_arch(triple)} vm"
258*bb4ee6a4SAndroid Build Coastguard Worker    elif dut == None:
259*bb4ee6a4SAndroid Build Coastguard Worker        dut_str = "[yellow]Skip[/yellow]"
260*bb4ee6a4SAndroid Build Coastguard Worker    else:
261*bb4ee6a4SAndroid Build Coastguard Worker        raise Exception(
262*bb4ee6a4SAndroid Build Coastguard Worker            f"--dut={dut} is not supported. Options are --dut=host or --dut=vm (linux only)"
263*bb4ee6a4SAndroid Build Coastguard Worker        )
264*bb4ee6a4SAndroid Build Coastguard Worker
265*bb4ee6a4SAndroid Build Coastguard Worker    skip_str = "[yellow]skip[/yellow]"
266*bb4ee6a4SAndroid Build Coastguard Worker    unit_test_str = "Run on host" if not no_unit_tests else skip_str
267*bb4ee6a4SAndroid Build Coastguard Worker    integration_test_str = dut_str if dut else skip_str
268*bb4ee6a4SAndroid Build Coastguard Worker    profile = os.environ.get("NEXTEST_PROFILE", "default")
269*bb4ee6a4SAndroid Build Coastguard Worker    console.print(f"Running tests for [green]{triple}[/green]")
270*bb4ee6a4SAndroid Build Coastguard Worker    console.print(f"Profile: [green]{profile}[/green]")
271*bb4ee6a4SAndroid Build Coastguard Worker    console.print(f"With features: [green]{features}[/green]")
272*bb4ee6a4SAndroid Build Coastguard Worker    console.print(f"no-default-features: [green]{no_default_features}[/green]")
273*bb4ee6a4SAndroid Build Coastguard Worker    console.print()
274*bb4ee6a4SAndroid Build Coastguard Worker    console.print(f"  Unit tests:        [bold]{unit_test_str}[/bold]")
275*bb4ee6a4SAndroid Build Coastguard Worker    console.print(f"  Integration tests: [bold]{integration_test_str}[/bold]")
276*bb4ee6a4SAndroid Build Coastguard Worker    console.print()
277*bb4ee6a4SAndroid Build Coastguard Worker
278*bb4ee6a4SAndroid Build Coastguard Worker    check_build_prerequisites(triple)
279*bb4ee6a4SAndroid Build Coastguard Worker
280*bb4ee6a4SAndroid Build Coastguard Worker    # Print tips in certain configurations.
281*bb4ee6a4SAndroid Build Coastguard Worker    if dut and not run_root_tests:
282*bb4ee6a4SAndroid Build Coastguard Worker        console.print(
283*bb4ee6a4SAndroid Build Coastguard Worker            "[green]Tip:[/green] Skipping tests that require root privileges. "
284*bb4ee6a4SAndroid Build Coastguard Worker            + "Use [bold]--run-root-tests[/bold] to enable them."
285*bb4ee6a4SAndroid Build Coastguard Worker        )
286*bb4ee6a4SAndroid Build Coastguard Worker    if not dut:
287*bb4ee6a4SAndroid Build Coastguard Worker        console.print(
288*bb4ee6a4SAndroid Build Coastguard Worker            "[green]Tip:[/green] To run integration tests on a built-in VM: "
289*bb4ee6a4SAndroid Build Coastguard Worker            + "Use [bold]--dut=vm[/bold] (preferred)"
290*bb4ee6a4SAndroid Build Coastguard Worker        )
291*bb4ee6a4SAndroid Build Coastguard Worker        console.print(
292*bb4ee6a4SAndroid Build Coastguard Worker            "[green]Tip:[/green] To run integration tests on the host: Use "
293*bb4ee6a4SAndroid Build Coastguard Worker            + "[bold]--dut=host[/bold] (fast, but unreliable)"
294*bb4ee6a4SAndroid Build Coastguard Worker        )
295*bb4ee6a4SAndroid Build Coastguard Worker    if dut == "vm":
296*bb4ee6a4SAndroid Build Coastguard Worker        vm_arch = get_vm_arch(triple)
297*bb4ee6a4SAndroid Build Coastguard Worker        if vm_arch == "x86_64":
298*bb4ee6a4SAndroid Build Coastguard Worker            cli_tool = "tools/x86vm"
299*bb4ee6a4SAndroid Build Coastguard Worker        elif vm_arch == "aarch64":
300*bb4ee6a4SAndroid Build Coastguard Worker            cli_tool = "tools/aarch64vm"
301*bb4ee6a4SAndroid Build Coastguard Worker        else:
302*bb4ee6a4SAndroid Build Coastguard Worker            raise Exception(f"Unknown vm arch '{vm_arch}'")
303*bb4ee6a4SAndroid Build Coastguard Worker        console.print(
304*bb4ee6a4SAndroid Build Coastguard Worker            f"[green]Tip:[/green] The test VM will remain alive between tests. You can manage this VM with [bold]{cli_tool}[/bold]"
305*bb4ee6a4SAndroid Build Coastguard Worker        )
306*bb4ee6a4SAndroid Build Coastguard Worker
307*bb4ee6a4SAndroid Build Coastguard Worker    # Prepare the dut for test execution
308*bb4ee6a4SAndroid Build Coastguard Worker    if dut == "host":
309*bb4ee6a4SAndroid Build Coastguard Worker        check_host_prerequisites(run_root_tests)
310*bb4ee6a4SAndroid Build Coastguard Worker    if dut == "vm":
311*bb4ee6a4SAndroid Build Coastguard Worker        # Start VM ahead of time but don't wait for it to boot.
312*bb4ee6a4SAndroid Build Coastguard Worker        testvm.up(get_vm_arch(triple))
313*bb4ee6a4SAndroid Build Coastguard Worker
314*bb4ee6a4SAndroid Build Coastguard Worker    nextest_args = [
315*bb4ee6a4SAndroid Build Coastguard Worker        f"--profile={profile}" if profile else None,
316*bb4ee6a4SAndroid Build Coastguard Worker        "--verbose" if verbose() else None,
317*bb4ee6a4SAndroid Build Coastguard Worker    ]
318*bb4ee6a4SAndroid Build Coastguard Worker
319*bb4ee6a4SAndroid Build Coastguard Worker    console.print()
320*bb4ee6a4SAndroid Build Coastguard Worker    console.rule("Building tests")
321*bb4ee6a4SAndroid Build Coastguard Worker
322*bb4ee6a4SAndroid Build Coastguard Worker    if triple == Triple.from_shorthand("riscv64"):
323*bb4ee6a4SAndroid Build Coastguard Worker        nextest_args += ["--exclude=" + s for s in DO_NOT_BUILD_RISCV64]
324*bb4ee6a4SAndroid Build Coastguard Worker
325*bb4ee6a4SAndroid Build Coastguard Worker    nextest_run = configure_cargo(
326*bb4ee6a4SAndroid Build Coastguard Worker        cmd("cargo nextest run"), triple, features, no_default_features
327*bb4ee6a4SAndroid Build Coastguard Worker    ).with_args(*nextest_args)
328*bb4ee6a4SAndroid Build Coastguard Worker
329*bb4ee6a4SAndroid Build Coastguard Worker    with record_time("Build"):
330*bb4ee6a4SAndroid Build Coastguard Worker        returncode = nextest_run.with_args("--no-run").fg(
331*bb4ee6a4SAndroid Build Coastguard Worker            style=Styles.live_truncated(), check=False
332*bb4ee6a4SAndroid Build Coastguard Worker        )
333*bb4ee6a4SAndroid Build Coastguard Worker        if returncode != 0:
334*bb4ee6a4SAndroid Build Coastguard Worker            sys.exit(returncode)
335*bb4ee6a4SAndroid Build Coastguard Worker
336*bb4ee6a4SAndroid Build Coastguard Worker    if not no_unit_tests:
337*bb4ee6a4SAndroid Build Coastguard Worker        unit_test_filter = copy.deepcopy(test_filter).exclude(*E2E_TESTS).include("kind(bench)")
338*bb4ee6a4SAndroid Build Coastguard Worker        if triple == Triple.from_shorthand("mingw64") and os.name == "posix":
339*bb4ee6a4SAndroid Build Coastguard Worker            unit_test_filter = unit_test_filter.exclude(*DO_NOT_RUN_WINE64)
340*bb4ee6a4SAndroid Build Coastguard Worker        console.print()
341*bb4ee6a4SAndroid Build Coastguard Worker        console.rule("Running unit tests")
342*bb4ee6a4SAndroid Build Coastguard Worker        with record_time("Unit Tests"):
343*bb4ee6a4SAndroid Build Coastguard Worker            for i in range(repetitions):
344*bb4ee6a4SAndroid Build Coastguard Worker                if repetitions > 1:
345*bb4ee6a4SAndroid Build Coastguard Worker                    console.rule(f"Round {i}", style="grey")
346*bb4ee6a4SAndroid Build Coastguard Worker
347*bb4ee6a4SAndroid Build Coastguard Worker                returncode = nextest_run.with_args("--lib --bins", *unit_test_filter.to_args()).fg(
348*bb4ee6a4SAndroid Build Coastguard Worker                    style=Styles.live_truncated(), check=False
349*bb4ee6a4SAndroid Build Coastguard Worker                )
350*bb4ee6a4SAndroid Build Coastguard Worker                if returncode != 0:
351*bb4ee6a4SAndroid Build Coastguard Worker                    sys.exit(returncode)
352*bb4ee6a4SAndroid Build Coastguard Worker
353*bb4ee6a4SAndroid Build Coastguard Worker    if dut:
354*bb4ee6a4SAndroid Build Coastguard Worker        package_dir = triple.target_dir / PACKAGE_NAME
355*bb4ee6a4SAndroid Build Coastguard Worker        package_archive = package_dir.with_suffix(".tar.zst")
356*bb4ee6a4SAndroid Build Coastguard Worker        nextest_package = configure_cargo(
357*bb4ee6a4SAndroid Build Coastguard Worker            cmd(TOOLS_ROOT / "nextest_package"), triple, features, no_default_features
358*bb4ee6a4SAndroid Build Coastguard Worker        )
359*bb4ee6a4SAndroid Build Coastguard Worker
360*bb4ee6a4SAndroid Build Coastguard Worker        test_exclusions = [*DO_NOT_RUN]
361*bb4ee6a4SAndroid Build Coastguard Worker        if not run_root_tests:
362*bb4ee6a4SAndroid Build Coastguard Worker            test_exclusions += ROOT_TESTS
363*bb4ee6a4SAndroid Build Coastguard Worker        if triple == Triple.from_shorthand("mingw64"):
364*bb4ee6a4SAndroid Build Coastguard Worker            test_exclusions += DO_NOT_RUN_WIN64
365*bb4ee6a4SAndroid Build Coastguard Worker            if os.name == "posix":
366*bb4ee6a4SAndroid Build Coastguard Worker                test_exclusions += DO_NOT_RUN_WINE64
367*bb4ee6a4SAndroid Build Coastguard Worker        if triple == Triple.from_shorthand("aarch64"):
368*bb4ee6a4SAndroid Build Coastguard Worker            test_exclusions += DO_NOT_RUN_AARCH64
369*bb4ee6a4SAndroid Build Coastguard Worker        test_filter = test_filter.exclude(*test_exclusions)
370*bb4ee6a4SAndroid Build Coastguard Worker
371*bb4ee6a4SAndroid Build Coastguard Worker        console.print()
372*bb4ee6a4SAndroid Build Coastguard Worker        console.rule("Packaging integration tests")
373*bb4ee6a4SAndroid Build Coastguard Worker        with record_time("Packing"):
374*bb4ee6a4SAndroid Build Coastguard Worker            nextest_package(
375*bb4ee6a4SAndroid Build Coastguard Worker                "--test *",
376*bb4ee6a4SAndroid Build Coastguard Worker                f"-d {package_dir}",
377*bb4ee6a4SAndroid Build Coastguard Worker                f"-o {package_archive}" if dut != "host" else None,
378*bb4ee6a4SAndroid Build Coastguard Worker                "--no-strip" if no_strip else None,
379*bb4ee6a4SAndroid Build Coastguard Worker                *test_filter.to_args(),
380*bb4ee6a4SAndroid Build Coastguard Worker                "--verbose" if verbose() else None,
381*bb4ee6a4SAndroid Build Coastguard Worker            ).fg(style=Styles.live_truncated())
382*bb4ee6a4SAndroid Build Coastguard Worker
383*bb4ee6a4SAndroid Build Coastguard Worker        target: Union[HostTarget, SshTarget]
384*bb4ee6a4SAndroid Build Coastguard Worker        if dut == "host":
385*bb4ee6a4SAndroid Build Coastguard Worker            target = HostTarget(package_dir)
386*bb4ee6a4SAndroid Build Coastguard Worker        elif dut == "vm":
387*bb4ee6a4SAndroid Build Coastguard Worker            testvm.up(get_vm_arch(triple), wait=True)
388*bb4ee6a4SAndroid Build Coastguard Worker            remote = Remote("localhost", testvm.ssh_opts(get_vm_arch(triple)))
389*bb4ee6a4SAndroid Build Coastguard Worker            target = SshTarget(package_archive, remote)
390*bb4ee6a4SAndroid Build Coastguard Worker
391*bb4ee6a4SAndroid Build Coastguard Worker        console.print()
392*bb4ee6a4SAndroid Build Coastguard Worker        console.rule("Running integration tests")
393*bb4ee6a4SAndroid Build Coastguard Worker        with record_time("Integration tests"):
394*bb4ee6a4SAndroid Build Coastguard Worker            for i in range(repetitions):
395*bb4ee6a4SAndroid Build Coastguard Worker                if repetitions > 1:
396*bb4ee6a4SAndroid Build Coastguard Worker                    console.rule(f"Round {i}", style="grey")
397*bb4ee6a4SAndroid Build Coastguard Worker                returncode = target.run_tests(
398*bb4ee6a4SAndroid Build Coastguard Worker                    [
399*bb4ee6a4SAndroid Build Coastguard Worker                        *test_filter.to_args(),
400*bb4ee6a4SAndroid Build Coastguard Worker                        *nextest_args,
401*bb4ee6a4SAndroid Build Coastguard Worker                        "--test-threads=1" if no_parallel else None,
402*bb4ee6a4SAndroid Build Coastguard Worker                    ]
403*bb4ee6a4SAndroid Build Coastguard Worker                )
404*bb4ee6a4SAndroid Build Coastguard Worker                if returncode != 0:
405*bb4ee6a4SAndroid Build Coastguard Worker                    if not no_parallel:
406*bb4ee6a4SAndroid Build Coastguard Worker                        console.print(
407*bb4ee6a4SAndroid Build Coastguard Worker                            "[green]Tip:[/green] Tests may fail when run in parallel on some platforms. "
408*bb4ee6a4SAndroid Build Coastguard Worker                            + "Try re-running with `--no-parallel`"
409*bb4ee6a4SAndroid Build Coastguard Worker                        )
410*bb4ee6a4SAndroid Build Coastguard Worker                    if dut == "host":
411*bb4ee6a4SAndroid Build Coastguard Worker                        console.print(
412*bb4ee6a4SAndroid Build Coastguard Worker                            f"[yellow]Tip:[/yellow] Running tests on the host may not be reliable. "
413*bb4ee6a4SAndroid Build Coastguard Worker                            "Prefer [bold]--dut=vm[/bold]."
414*bb4ee6a4SAndroid Build Coastguard Worker                        )
415*bb4ee6a4SAndroid Build Coastguard Worker                    sys.exit(returncode)
416*bb4ee6a4SAndroid Build Coastguard Worker
417*bb4ee6a4SAndroid Build Coastguard Worker
418*bb4ee6a4SAndroid Build Coastguard Workerif __name__ == "__main__":
419*bb4ee6a4SAndroid Build Coastguard Worker    run_main(main)
420