1use crate::common::CommonArch;
11use crate::common::CommonProfile;
12use crate::common::CommonTriple;
13use crate::run_cargo_nextest_run::NextestProfile;
14use flowey::node::prelude::*;
15use flowey_lib_common::run_cargo_build::CargoFeatureSet;
16use flowey_lib_common::run_cargo_nextest_run::TestResults;
17use flowey_lib_common::run_cargo_nextest_run::build_params::NextestBuildParams;
18use flowey_lib_common::run_cargo_nextest_run::build_params::TestPackages;
19use std::collections::BTreeMap;
20
21#[derive(Serialize, Deserialize)]
23pub struct NextestUnitTestArchive {
24 #[serde(rename = "unit_tests.tar.zst")]
25 pub archive_file: PathBuf,
26}
27
28#[derive(Serialize, Deserialize)]
30pub enum BuildNextestUnitTestMode {
31 ImmediatelyRun {
34 nextest_profile: NextestProfile,
35 junit_test_label: String,
39 artifact_dir: Option<ReadVar<PathBuf>>,
42 results: WriteVar<Vec<TestResults>>,
44 publish_done: WriteVar<SideEffect>,
46 },
47 Archive(WriteVar<Vec<NextestUnitTestArchive>>),
50}
51
52flowey_request! {
53 pub struct Request {
54 pub target: target_lexicon::Triple,
56 pub profile: CommonProfile,
58 pub build_mode: BuildNextestUnitTestMode,
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::build_xtask::Node>();
70 ctx.import::<crate::git_checkout_openvmm_repo::Node>();
71 ctx.import::<crate::init_openvmm_magicpath_openhcl_sysroot::Node>();
72 ctx.import::<crate::install_openvmm_rust_build_essential::Node>();
73 ctx.import::<crate::run_cargo_nextest_run::Node>();
74 ctx.import::<crate::init_cross_build::Node>();
75 ctx.import::<flowey_lib_common::run_cargo_nextest_archive::Node>();
76 ctx.import::<flowey_lib_common::publish_test_results::Node>();
77 }
78
79 fn emit(requests: Vec<Self::Request>, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
80 let xtask_target = CommonTriple::Common {
81 arch: ctx.arch().try_into()?,
82 platform: ctx.platform().try_into()?,
83 };
84 let xtask = ctx.reqv(|v| crate::build_xtask::Request {
85 target: xtask_target,
86 xtask: v,
87 });
88
89 let openvmm_repo_path = ctx.reqv(crate::git_checkout_openvmm_repo::req::GetRepoDir);
90
91 let ambient_deps = vec![ctx.reqv(crate::install_openvmm_rust_build_essential::Request)];
94
95 let test_packages = ctx.emit_rust_stepv("determine unit test exclusions", |ctx| {
96 let xtask = xtask.claim(ctx);
97 let openvmm_repo_path = openvmm_repo_path.clone().claim(ctx);
98 move |rt| {
99 let xtask = rt.read(xtask);
100 let openvmm_repo_path = rt.read(openvmm_repo_path);
101
102 let mut exclude = [
103 "vmm_tests",
105 "cca_tests",
107 "guest_test_uefi",
109 "inspect_derive",
116 "mesh_derive",
117 "save_restore_derive",
118 "test_with_tracing_macro",
119 "pal_async_test",
120 "vmm_test_macros",
121 ]
122 .map(|x| x.to_string())
123 .to_vec();
124
125 {
128 let xtask_bin = match xtask {
129 crate::build_xtask::XtaskOutput::LinuxBin { bin, dbg: _ } => bin,
130 crate::build_xtask::XtaskOutput::WindowsBin { exe, pdb: _ } => exe,
131 };
132
133 rt.sh.change_dir(openvmm_repo_path);
134 let output =
135 flowey::shell_cmd!(rt, "{xtask_bin} fuzz list --crates").output()?;
136 let output = String::from_utf8(output.stdout)?;
137
138 let fuzz_crates = output.trim().split('\n').map(|s| s.to_owned());
139 exclude.extend(fuzz_crates);
140 }
141
142 Ok(TestPackages::Workspace { exclude })
143 }
144 });
145
146 for Request {
147 target,
148 profile,
149 build_mode,
150 } in requests
151 {
152 let mut pre_run_deps = ambient_deps.clone();
153
154 let sysroot_arch = CommonArch::from_architecture(target.architecture)?;
155
156 if matches!(target.environment, target_lexicon::Environment::Musl) {
160 pre_run_deps.push(
161 ctx.reqv(|v| crate::init_openvmm_magicpath_openhcl_sysroot::Request {
162 arch: sysroot_arch,
163 path: v,
164 })
165 .into_side_effect(),
166 );
167 }
168
169 let features = if matches!(
175 target.operating_system,
176 target_lexicon::OperatingSystem::Windows
177 ) {
178 CargoFeatureSet::Specific(vec!["ci".into()])
179 } else {
180 CargoFeatureSet::All
181 };
182
183 let injected_env = ctx.reqv(|v| crate::init_cross_build::Request {
184 target: target.clone(),
185 injected_env: v,
186 });
187
188 let base_build_params = NextestBuildParams {
189 packages: test_packages.clone(),
190 features,
191 no_default_features: false,
192 target: target.clone(),
193 profile: profile.into(),
194 extra_env: injected_env,
195 };
196
197 let mut runs: Vec<(String, NextestBuildParams)> =
199 vec![("base".into(), base_build_params.clone())];
200
201 let mut crypto_feature_sets = vec![
210 ("native", CargoFeatureSet::Specific(vec!["native".into()])),
211 ("rust", CargoFeatureSet::Specific(vec!["rust".into()])),
212 ];
213 if matches!(
214 target.operating_system,
215 target_lexicon::OperatingSystem::Linux
216 ) {
217 crypto_feature_sets
218 .push(("openssl", CargoFeatureSet::Specific(vec!["openssl".into()])));
219 if matches!(target.environment, target_lexicon::Environment::Musl) {
221 crypto_feature_sets.push((
222 "symcrypt",
223 CargoFeatureSet::Specific(vec!["symcrypt".into()]),
224 ));
225 }
226 crypto_feature_sets.push(("all", CargoFeatureSet::All));
227 }
228 for (name, features) in crypto_feature_sets {
229 runs.push((
230 format!("crypto-{}", name),
231 NextestBuildParams {
232 packages: ReadVar::from_static(TestPackages::Crates {
233 crates: vec!["crypto".into()],
234 }),
235 features,
236 ..base_build_params.clone()
237 },
238 ));
239 }
240
241 match build_mode {
242 BuildNextestUnitTestMode::ImmediatelyRun {
243 nextest_profile,
244 junit_test_label,
245 artifact_dir,
246 results,
247 publish_done,
248 } => {
249 let test_results: Vec<_> = runs
250 .into_iter()
251 .map(|(friendly_name, build_params)| {
252 let test_label = format!("{junit_test_label}-{friendly_name}");
253 let r = ctx.reqv(|v| crate::run_cargo_nextest_run::Request {
254 friendly_name: test_label.clone(),
255 run_kind:
256 flowey_lib_common::run_cargo_nextest_run::NextestRunKind::BuildAndRun(
257 build_params,
258 ),
259 nextest_profile,
260 nextest_filter_expr: None,
261 nextest_working_dir: None,
262 nextest_config_file: None,
263 run_ignored: false,
264 extra_env: None,
265 pre_run_deps: pre_run_deps.clone(),
266 results: v,
267 });
268 (test_label, r)
269 })
270 .collect();
271
272 let publish_dones: Vec<_> = test_results
275 .iter()
276 .map(|(test_label, r)| {
277 ctx.reqv(|v| flowey_lib_common::publish_test_results::Request {
278 test_results: r.clone(),
279 test_label: test_label.clone(),
280 attachments: BTreeMap::new(),
281 output_dir: artifact_dir.clone(),
282 upload_logs_on_success: true,
283 done: v,
284 })
285 })
286 .collect();
287
288 ctx.emit_minor_rust_step("merge unit test results", |ctx| {
289 let test_results = test_results
290 .into_iter()
291 .map(|(_, r)| r.claim(ctx))
292 .collect::<Vec<_>>();
293 let results = results.claim(ctx);
294 move |rt| {
295 let flattened = test_results.into_iter().map(|t| rt.read(t)).collect();
296 rt.write(results, &flattened);
297 }
298 });
299
300 ctx.emit_side_effect_step(publish_dones, [publish_done]);
301 }
302 BuildNextestUnitTestMode::Archive(unit_tests_archive) => {
303 let archive_files: Vec<_> = runs
304 .into_iter()
305 .map(|(friendly_name, build_params)| {
306 ctx.reqv(|v| flowey_lib_common::run_cargo_nextest_archive::Request {
307 friendly_label: friendly_name,
308 working_dir: openvmm_repo_path.clone(),
309 build_params,
310 pre_run_deps: pre_run_deps.clone(),
311 archive_file: v,
312 })
313 })
314 .collect();
315
316 ctx.emit_minor_rust_step("report built unit tests", |ctx| {
317 let archive_files = archive_files.claim(ctx);
318 let unit_tests = unit_tests_archive.claim(ctx);
319 |rt| {
320 let flattened = archive_files
321 .into_iter()
322 .map(|t| NextestUnitTestArchive {
323 archive_file: rt.read(t),
324 })
325 .collect::<Vec<_>>();
326 rt.write(unit_tests, &flattened);
327 }
328 });
329 }
330 }
331 }
332
333 Ok(())
334 }
335}