flowey_lib_hvlite/
build_nextest_vmm_tests.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Build the cargo-nextest based VMM tests.
5
6use crate::run_cargo_build::common::CommonProfile;
7use crate::run_cargo_nextest_run::NextestProfile;
8use flowey::node::prelude::*;
9use flowey_lib_common::run_cargo_build::CargoBuildProfile;
10use flowey_lib_common::run_cargo_nextest_run::TestResults;
11use flowey_lib_common::run_cargo_nextest_run::build_params::FeatureSet;
12use flowey_lib_common::run_cargo_nextest_run::build_params::TestPackages;
13use std::collections::BTreeMap;
14
15/// Type-safe wrapper around a built nextest archive containing VMM tests
16#[derive(Serialize, Deserialize)]
17pub struct NextestVmmTestsArchive {
18    #[serde(rename = "vmm_tests.tar.zst")]
19    pub archive_file: PathBuf,
20}
21
22impl Artifact for NextestVmmTestsArchive {}
23
24/// Build mode to use when building the nextest VMM tests
25#[derive(Serialize, Deserialize)]
26pub enum BuildNextestVmmTestsMode {
27    /// Build and immediate run VMM tests, side-stepping any intermediate
28    /// archiving steps.
29    ///
30    /// NOTE: The caller is responsible for setting `extra_env` and
31    /// `pre_run_deps` to ensure that all tests filtered by
32    /// `nextest_filter_expr` are able to run successfully.
33    ImmediatelyRun {
34        /// Nextest profile to use when running the source code
35        nextest_profile: NextestProfile,
36        /// Nextest test filter expression
37        nextest_filter_expr: Option<String>,
38        /// Additional env vars set when executing the tests.
39        extra_env: ReadVar<BTreeMap<String, String>>,
40        /// Wait for specified side-effects to resolve before building / running
41        /// any tests. (e.g: to allow for some ambient packages / dependencies
42        /// to get installed).
43        pre_run_deps: Vec<ReadVar<SideEffect>>,
44
45        results: WriteVar<TestResults>,
46    },
47    /// Build and archive the tests into a nextest archive file, which can then
48    /// be run via [`crate::test_nextest_vmm_tests_archive`].
49    Archive(WriteVar<NextestVmmTestsArchive>),
50}
51
52flowey_request! {
53    pub struct Request {
54        /// Build and run VMM tests for the specified target
55        pub target: target_lexicon::Triple,
56        /// Build and run VMM tests with the specified cargo profile
57        pub profile: CommonProfile,
58        /// Build mode to use when building the nextest VMM tests
59        pub build_mode: BuildNextestVmmTestsMode,
60    }
61}
62
63new_flow_node!(struct Node);
64
65impl FlowNode for Node {
66    type Request = Request;
67
68    fn imports(ctx: &mut ImportCtx<'_>) {
69        ctx.import::<crate::install_openvmm_rust_build_essential::Node>();
70        ctx.import::<crate::run_cargo_nextest_run::Node>();
71        ctx.import::<crate::git_checkout_openvmm_repo::Node>();
72        ctx.import::<crate::init_openvmm_magicpath_openhcl_sysroot::Node>();
73        ctx.import::<crate::init_cross_build::Node>();
74        ctx.import::<flowey_lib_common::run_cargo_nextest_archive::Node>();
75    }
76
77    fn emit(requests: Vec<Self::Request>, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
78        // base requirements for building crates in the hvlite tree
79        let ambient_deps = vec![ctx.reqv(crate::install_openvmm_rust_build_essential::Request)];
80
81        for Request {
82            target,
83            profile,
84            build_mode,
85        } in requests
86        {
87            let mut ambient_deps = ambient_deps.clone();
88
89            let sysroot_arch = match target.architecture {
90                target_lexicon::Architecture::Aarch64(_) => {
91                    crate::init_openvmm_magicpath_openhcl_sysroot::OpenvmmSysrootArch::Aarch64
92                }
93                target_lexicon::Architecture::X86_64 => {
94                    crate::init_openvmm_magicpath_openhcl_sysroot::OpenvmmSysrootArch::X64
95                }
96                arch => anyhow::bail!("unsupported arch {arch}"),
97            };
98
99            // See comment in `crate::cargo_build` for why this is necessary.
100            //
101            // copied here since this node doesn't actually route through `cargo build`.
102            if matches!(target.environment, target_lexicon::Environment::Musl) {
103                ambient_deps.push(
104                    ctx.reqv(|v| crate::init_openvmm_magicpath_openhcl_sysroot::Request {
105                        arch: sysroot_arch,
106                        path: v,
107                    })
108                    .into_side_effect(),
109                );
110            }
111
112            let injected_env = ctx.reqv(|v| crate::init_cross_build::Request {
113                target: target.clone(),
114                injected_env: v,
115            });
116
117            let build_params =
118                flowey_lib_common::run_cargo_nextest_run::build_params::NextestBuildParams {
119                    packages: ReadVar::from_static(TestPackages::Crates {
120                        crates: vec!["vmm_tests".into()],
121                    }),
122                    features: FeatureSet::Specific(Vec::new()),
123                    no_default_features: false,
124                    unstable_panic_abort_tests: None, // don't run VMM tests on musl hvlite
125                    target: target.clone(),
126                    profile: match profile {
127                        CommonProfile::Release => CargoBuildProfile::Release,
128                        CommonProfile::Debug => CargoBuildProfile::Debug,
129                    },
130                    extra_env: injected_env,
131                };
132
133            match build_mode {
134                BuildNextestVmmTestsMode::ImmediatelyRun {
135                    nextest_profile,
136                    nextest_filter_expr,
137                    extra_env,
138                    pre_run_deps,
139                    results,
140                } => {
141                    ambient_deps.extend(pre_run_deps);
142
143                    ctx.req(crate::run_cargo_nextest_run::Request {
144                        friendly_name: "vmm_tests".into(),
145                        run_kind:
146                            flowey_lib_common::run_cargo_nextest_run::NextestRunKind::BuildAndRun(
147                                build_params,
148                            ),
149                        nextest_profile,
150                        nextest_filter_expr,
151                        nextest_working_dir: None,
152                        nextest_config_file: None,
153                        run_ignored: false,
154                        extra_env: Some(extra_env),
155                        pre_run_deps: ambient_deps,
156                        results,
157                    })
158                }
159                BuildNextestVmmTestsMode::Archive(unit_tests_archive) => {
160                    let openvmm_repo_path =
161                        ctx.reqv(crate::git_checkout_openvmm_repo::req::GetRepoDir);
162
163                    let archive_file =
164                        ctx.reqv(|v| flowey_lib_common::run_cargo_nextest_archive::Request {
165                            friendly_label: "vmm_tests".into(),
166                            working_dir: openvmm_repo_path,
167                            build_params,
168                            pre_run_deps: ambient_deps,
169                            archive_file: v,
170                        });
171
172                    ctx.emit_minor_rust_step("report built vmm_tests", |ctx| {
173                        let archive_file = archive_file.claim(ctx);
174                        let unit_tests = unit_tests_archive.claim(ctx);
175                        |rt| {
176                            let archive_file = rt.read(archive_file);
177                            rt.write(unit_tests, &NextestVmmTestsArchive { archive_file });
178                        }
179                    });
180                }
181            }
182        }
183
184        Ok(())
185    }
186}