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