Skip to main content

flowey_hvlite/pipelines/
vmm_tests_run_target.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Run VMM tests on a target system with artifacts built by `VmmTestsRun`.
5
6use crate::pipelines::vmm_tests_run::VmmTestTargetCli;
7use crate::pipelines::vmm_tests_run::resolve_incubator;
8use crate::pipelines::vmm_tests_run::resolve_target;
9use anyhow::Context;
10use flowey::node::prelude::ReadVar;
11use flowey::pipeline::prelude::*;
12use flowey_lib_hvlite::init_vmm_tests_env::PetriParams;
13use flowey_lib_hvlite::install_vmm_tests_external_deps::VmmTestsExternalDeps;
14use flowey_lib_hvlite::install_vmm_tests_external_deps::VmmTestsExternalDepsLinux;
15use flowey_lib_hvlite::install_vmm_tests_external_deps::VmmTestsExternalDepsWindows;
16use std::num::NonZeroU64;
17use std::path::PathBuf;
18use vmm_test_images::KnownTestArtifacts;
19
20/// Run VMM tests on a target system with artifacts built by `VmmTestsRun`.
21#[derive(clap::Args)]
22pub struct VmmTestsRunTargetCli {
23    /// Specify what target to build the VMM tests for
24    ///
25    /// If not specified, defaults to the current host target.
26    #[clap(long)]
27    target: Option<VmmTestTargetCli>,
28
29    /// Directory for the output artifacts.
30    #[clap(long)]
31    dir: PathBuf,
32
33    /// Test filter (nextest filter expression)
34    #[clap(long, default_value = "all()")]
35    filter: String,
36
37    /// The test artifacts to download.
38    #[clap(long, value_delimiter = ',')]
39    artifacts: Vec<KnownTestArtifacts>,
40
41    /// Prep steps variants to run
42    #[clap(long, value_delimiter = ',')]
43    prep_steps: Vec<String>,
44
45    /// pass `--verbose` to cargo
46    #[clap(long)]
47    verbose: bool,
48
49    /// Automatically install any missing required dependencies.
50    #[clap(long)]
51    install_missing_deps: bool,
52
53    /// Skip the interactive VHD download prompt
54    #[clap(long)]
55    skip_vhd_prompt: bool,
56
57    /// use the nextest CI profile rather than the default one
58    #[clap(long)]
59    ci_profile: bool,
60
61    /// Don't reuse prepped vhds, even if they already exist.
62    /// Use when making changes to prep_steps
63    #[clap(long)]
64    no_reuse_prepped_vhds: bool,
65
66    /// Whether the tests selected require hardware isolation
67    #[clap(long)]
68    needs_hardware_isolation: bool,
69
70    /// Whether the tests selected require the test igvm agent
71    #[clap(long)]
72    needs_igvm_agent: bool,
73
74    /// How many times to run the tests
75    #[clap(long)]
76    repetitions: Option<u64>,
77
78    /// Run tests inside an emulated incubator.
79    #[clap(long, num_args = 0..=1)]
80    #[expect(clippy::option_option)]
81    incubator: Option<Option<PathBuf>>,
82}
83
84impl IntoPipeline for VmmTestsRunTargetCli {
85    fn into_pipeline(self, backend_hint: PipelineBackendHint) -> anyhow::Result<Pipeline> {
86        if !matches!(backend_hint, PipelineBackendHint::Local) {
87            anyhow::bail!("vmm-tests-run-target is for local use only")
88        }
89
90        let Self {
91            target,
92            dir,
93            filter,
94            artifacts,
95            prep_steps,
96            verbose,
97            install_missing_deps,
98            skip_vhd_prompt,
99            ci_profile,
100            no_reuse_prepped_vhds,
101            needs_hardware_isolation,
102            needs_igvm_agent,
103            repetitions,
104            incubator,
105        } = self;
106
107        // When --incubator is set, --target must also be specified
108        // to indicate the cross-compilation target for the incubator.
109        if incubator.is_some() && target.is_none() {
110            anyhow::bail!("--incubator requires --target (e.g., --target linux-aarch64-musl)");
111        }
112
113        let repetitions =
114            NonZeroU64::new(repetitions.unwrap_or(1)).context("repetitions must not be zero")?;
115
116        let target = resolve_target(target, backend_hint)?;
117
118        let incubator_profile = incubator
119            .map(|i| resolve_incubator(i, &target))
120            .transpose()?;
121
122        let external_deps = match target.as_triple().operating_system {
123            target_lexicon::OperatingSystem::Windows => {
124                VmmTestsExternalDeps::Windows(VmmTestsExternalDepsWindows {
125                    hyperv: true, // TODO
126                    whp: true,    // TODO
127                    hardware_isolation: needs_hardware_isolation,
128                })
129            }
130            target_lexicon::OperatingSystem::Linux => {
131                VmmTestsExternalDeps::Linux(VmmTestsExternalDepsLinux {
132                    hugetlb_2mb_overcommit_pages: None, // TODO
133                    prepare_vhost_vsock: false,         // TODO
134                })
135            }
136            _ => unreachable!(),
137        };
138
139        let mut pipeline = Pipeline::new();
140
141        let mut job = pipeline.new_job(
142            FlowPlatform::host(backend_hint),
143            FlowArch::host(backend_hint),
144            "run vmm tests on target system",
145        );
146
147        job = job.dep_on(|_| flowey_lib_hvlite::_jobs::cfg_versions::Request::Init);
148
149        job = job
150            .dep_on(|_| flowey_lib_hvlite::_jobs::cfg_common::Params {
151                local_only: Some(flowey_lib_hvlite::_jobs::cfg_common::LocalOnlyParams {
152                    interactive: true,
153                    auto_install: install_missing_deps,
154                    ignore_rust_version: true,
155                }),
156                verbose: ReadVar::from_static(verbose),
157                locked: false,
158                deny_warnings: false,
159                no_incremental: false,
160            })
161            .dep_on(
162                |ctx| flowey_lib_hvlite::_jobs::local_run_nextest_vmm_tests::Params {
163                    target,
164                    test_content_dir: dir,
165                    filter,
166                    downloaded_artifacts: artifacts,
167                    external_deps,
168                    prep_steps_variants: prep_steps,
169                    needs_test_igvm_agent_rpc_server: needs_igvm_agent,
170                    skip_vhd_prompt,
171                    nextest_profile: if ci_profile {
172                        flowey_lib_hvlite::run_cargo_nextest_run::NextestProfile::Ci
173                    } else {
174                        flowey_lib_hvlite::run_cargo_nextest_run::NextestProfile::Default
175                    },
176                    petri_params: PetriParams {
177                        disable_remote_artifacts: false,
178                        reuse_prepped_vhds: !no_reuse_prepped_vhds,
179                        require_2mb_hugetlb: false, // TODO
180                    },
181                    repetitions,
182                    incubator_profile,
183                    done: ctx.new_done_handle(),
184                },
185            );
186
187        job.finish();
188
189        Ok(pipeline)
190    }
191}