From dc00c7d9607e850d46bc78bfb19babd811916aa4 Mon Sep 17 00:00:00 2001 From: John Starks Date: Mon, 3 Feb 2025 11:13:18 -0800 Subject: [PATCH] vmm_tests: use libtest-mimic to define and run tests (#762) Instead of using the built-in test harness, use libtest-mimic to define tests. This makes tests easier to define: * We can define tests programmatically at runtime instead of just statically at compile time; this will make it possible to reduce/eliminate the use of the domain-specific language in the `vmm_test` macro to define test constraints (supported VMMs, architectures, etc.). * We can define and query artifacts requirements separately from running the tests, making it easier to integrate with flowey. --- Cargo.lock | 38 +++- Cargo.toml | 1 + petri/Cargo.toml | 3 + petri/petri_artifacts_core/src/lib.rs | 76 ++++--- petri/src/disk_image.rs | 6 +- petri/src/lib.rs | 9 +- petri/src/test.rs | 193 ++++++++++++++++++ petri/src/vm/hyperv/mod.rs | 20 +- petri/src/vm/mod.rs | 19 -- petri/src/vm/openvmm/construct.rs | 68 +++--- petri/src/vm/openvmm/mod.rs | 2 +- petri/src/vm/openvmm/modify.rs | 2 +- petri/src/vm/openvmm/runtime.rs | 8 +- petri/src/vm/openvmm/start.rs | 8 +- .../src/lib.rs | 26 ++- vmm_tests/vmm_test_macros/src/lib.rs | 46 +++-- vmm_tests/vmm_test_petri_support/Cargo.toml | 3 - vmm_tests/vmm_test_petri_support/src/lib.rs | 43 +--- vmm_tests/vmm_tests/Cargo.toml | 4 + vmm_tests/vmm_tests/tests/tests/main.rs | 28 +-- vmm_tests/vmm_tests/tests/tests/ttrpc.rs | 28 +-- 21 files changed, 411 insertions(+), 220 deletions(-) create mode 100644 petri/src/test.rs diff --git a/Cargo.lock b/Cargo.lock index 335b4390a7..02e2b8e447 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,23 +90,24 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.11" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" @@ -1501,6 +1502,12 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "281e452d3bad4005426416cdba5ccfd4f5c1280e10099e21db27f7c1c28347fc" +[[package]] +name = "escape8259" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5692dd7b5a1978a5aeb0ce83b7655c58ca8efdcb79d21036ea249da95afec2c6" + [[package]] name = "event-listener" version = "5.3.1" @@ -3378,6 +3385,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -3498,6 +3511,18 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "libtest-mimic" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5297962ef19edda4ce33aaa484386e0a5b3d7f2f4e037cbeee00503ef6b29d33" +dependencies = [ + "anstream", + "anstyle", + "clap", + "escape8259", +] + [[package]] name = "linkme" version = "0.3.31" @@ -5083,6 +5108,7 @@ dependencies = [ "anyhow", "async-trait", "chipset_resources", + "clap", "diag_client", "disk_backend_resources", "disk_vhd1", @@ -5103,6 +5129,8 @@ dependencies = [ "ide_resources", "image", "inspect", + "libtest-mimic", + "linkme", "mbrman", "mesh", "mesh_process", @@ -8423,8 +8451,6 @@ dependencies = [ name = "vmm_test_petri_support" version = "0.0.0" dependencies = [ - "anyhow", - "parking_lot", "petri", "petri_artifacts_common", "petri_artifacts_vmm_test", diff --git a/Cargo.toml b/Cargo.toml index b1a823f28b..f6aaba3b68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -448,6 +448,7 @@ kvm-bindings = "0.7" landlock = "0.3.1" libc = "0.2" libfuzzer-sys = "0.4" +libtest-mimic = "0.8" linkme = "0.3.9" log = "0.4" macaddr = "1.0" diff --git a/petri/Cargo.toml b/petri/Cargo.toml index ccd5c8b474..ca02b752fb 100644 --- a/petri/Cargo.toml +++ b/petri/Cargo.toml @@ -57,6 +57,7 @@ sparse_mmap.workspace = true anyhow.workspace = true async-trait.workspace = true +clap.workspace = true fatfs = { workspace = true, features = ["std", "alloc"] } fs-err.workspace = true fscommon.workspace = true @@ -64,6 +65,8 @@ futures.workspace = true futures-concurrency.workspace = true gptman.workspace = true image = { workspace = true, features = ["png"] } +libtest-mimic.workspace = true +linkme.workspace = true mbrman.workspace = true prost.workspace = true tempfile.workspace = true diff --git a/petri/petri_artifacts_core/src/lib.rs b/petri/petri_artifacts_core/src/lib.rs index 32884919c9..d22a51fc6f 100644 --- a/petri/petri_artifacts_core/src/lib.rs +++ b/petri/petri_artifacts_core/src/lib.rs @@ -16,6 +16,7 @@ use std::sync::Arc; // exported to support the `declare_artifacts!` macro #[doc(hidden)] pub use paste; +use std::path::Path; /// A trait that marks a type as being the type-safe ID for a petri artifact. /// @@ -141,38 +142,35 @@ macro_rules! declare_artifacts { }; } -/// An object-safe trait used to abstract details of artifact resolution. +/// A trait to resolve artifacts to paths. /// -/// Test authors are expected to use the [`TestArtifactResolver`] and -/// [`TestArtifacts`] abstractions to interact with resolvers, and should not +/// Test authors are expected to use the [`TestArtifactRequirements`] and +/// [`TestArtifacts`] abstractions to interact with artifacts, and should not /// use this API directly. -pub trait TestArtifactResolverBackend { +pub trait ResolveTestArtifact { /// Given an artifact handle, return its corresponding PathBuf. /// /// This method must use type-erased handles, as using typed artifact /// handles in this API would cause the trait to no longer be object-safe. fn resolve(&self, id: ErasedArtifactHandle) -> anyhow::Result; +} - /// Invoked once all calls to `resolve` has gone through. - /// - /// Callers must ensure that this method is called after their final call to - /// `resolve`. By doing this, it is possible to implement "dry-run" - /// resolvers, which simply list a test's required dependencies, and then - /// terminate the test run. - fn finalize(self: Box) {} +impl ResolveTestArtifact for &T { + fn resolve(&self, id: ErasedArtifactHandle) -> anyhow::Result { + (**self).resolve(id) + } } /// A set of dependencies required to run a test. -pub struct TestArtifactResolver { - backend: Box, +#[derive(Clone)] +pub struct TestArtifactRequirements { artifacts: Vec<(ErasedArtifactHandle, bool)>, } -impl TestArtifactResolver { +impl TestArtifactRequirements { /// Create an empty set of dependencies. - pub fn new(backend: Box) -> Self { - TestArtifactResolver { - backend, + pub fn new() -> Self { + TestArtifactRequirements { artifacts: Vec::new(), } } @@ -189,13 +187,27 @@ impl TestArtifactResolver { self } - /// Finalize the set of dependencies. - pub fn finalize(self) -> TestArtifacts { + /// Returns the current list of required depencencies. + pub fn required_artifacts(&self) -> impl Iterator + '_ { + self.artifacts + .iter() + .filter_map(|&(a, optional)| (!optional).then_some(a)) + } + + /// Returns the current list of optional dependencies. + pub fn optional_artifacts(&self) -> impl Iterator + '_ { + self.artifacts + .iter() + .filter_map(|&(a, optional)| optional.then_some(a)) + } + + /// Resolve the set of dependencies. + pub fn resolve(&self, resolver: impl ResolveTestArtifact) -> anyhow::Result { let mut failed = String::new(); let mut resolved = HashMap::new(); - for (a, optional) in self.artifacts { - match self.backend.resolve(a) { + for &(a, optional) in &self.artifacts { + match resolver.resolve(a) { Ok(p) => { resolved.insert(a, p); } @@ -204,36 +216,34 @@ impl TestArtifactResolver { } } - self.backend.finalize(); - if !failed.is_empty() { - panic!("Artifact resolution failed:\n{}", failed); + anyhow::bail!("Artifact resolution failed:\n{}", failed); } - TestArtifacts { + Ok(TestArtifacts { artifacts: Arc::new(resolved), - } + }) } } /// A resolved set of test artifacts, returned by -/// [`TestArtifactResolver::finalize`]. +/// [`TestArtifactRequirements::resolve`]. #[derive(Clone)] pub struct TestArtifacts { artifacts: Arc>, } impl TestArtifacts { - /// Try to resolve an artifact to a path. + /// Try to get the resolved path of an artifact. #[track_caller] - pub fn try_resolve(&self, artifact: impl AsArtifactHandle) -> Option { - self.artifacts.get(&artifact.erase()).cloned() + pub fn try_get(&self, artifact: impl AsArtifactHandle) -> Option<&Path> { + self.artifacts.get(&artifact.erase()).map(|p| p.as_ref()) } - /// Resolve an artifact to a path. + /// Get the resolved path of an artifact. #[track_caller] - pub fn resolve(&self, artifact: impl AsArtifactHandle) -> PathBuf { - self.try_resolve(artifact.erase()) + pub fn get(&self, artifact: impl AsArtifactHandle) -> &Path { + self.try_get(artifact.erase()) .unwrap_or_else(|| panic!("Artifact not initially required: {:?}", artifact.erase())) } } diff --git a/petri/src/disk_image.rs b/petri/src/disk_image.rs index a6791fe19c..e32b9a7b0a 100644 --- a/petri/src/disk_image.rs +++ b/petri/src/disk_image.rs @@ -20,7 +20,7 @@ use std::path::Path; pub fn build_agent_image( arch: MachineArch, os_flavor: OsFlavor, - resolver: &TestArtifacts, + artifacts: &TestArtifacts, ) -> anyhow::Result { match os_flavor { OsFlavor::Windows => { @@ -30,7 +30,7 @@ pub fn build_agent_image( b"pipette ", &[( "pipette.exe", - PathOrBinary::Path(&resolver.resolve(match arch { + PathOrBinary::Path(artifacts.get(match arch { MachineArch::X86_64 => common_artifacts::PIPETTE_WINDOWS_X64.erase(), MachineArch::Aarch64 => common_artifacts::PIPETTE_WINDOWS_AARCH64.erase(), })), @@ -45,7 +45,7 @@ pub fn build_agent_image( &[ ( "pipette", - PathOrBinary::Path(&resolver.resolve(match arch { + PathOrBinary::Path(artifacts.get(match arch { MachineArch::X86_64 => common_artifacts::PIPETTE_LINUX_X64.erase(), MachineArch::Aarch64 => common_artifacts::PIPETTE_LINUX_AARCH64.erase(), })), diff --git a/petri/src/lib.rs b/petri/src/lib.rs index 4962073ffe..e3570fab1a 100644 --- a/petri/src/lib.rs +++ b/petri/src/lib.rs @@ -12,6 +12,7 @@ mod disk_image; mod linux_direct_serial_agent; mod openhcl_diag; +mod test; mod tracing; mod vm; mod worker; @@ -19,10 +20,14 @@ mod worker; pub use petri_artifacts_core::ArtifactHandle; pub use petri_artifacts_core::AsArtifactHandle; pub use petri_artifacts_core::ErasedArtifactHandle; -pub use petri_artifacts_core::TestArtifactResolver; -pub use petri_artifacts_core::TestArtifactResolverBackend; +pub use petri_artifacts_core::ResolveTestArtifact; +pub use petri_artifacts_core::TestArtifactRequirements; pub use petri_artifacts_core::TestArtifacts; pub use pipette_client as pipette; +pub use test::test_macro_support; +pub use test::test_main; +pub use test::RunTest; +pub use test::SimpleTest; pub use vm::*; /// 1 kibibyte's worth of bytes. diff --git a/petri/src/test.rs b/petri/src/test.rs new file mode 100644 index 0000000000..d07fd03ef5 --- /dev/null +++ b/petri/src/test.rs @@ -0,0 +1,193 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Infrastructure for defining tests. + +#[doc(hidden)] +pub mod test_macro_support { + use super::RunTest; + pub use linkme; + + #[linkme::distributed_slice] + pub static TESTS: [fn() -> (&'static str, Vec>)] = [..]; +} + +use crate::TestArtifactRequirements; +use crate::TestArtifacts; +use test_macro_support::TESTS; + +/// Defines a single test from a value that implements [`RunTest`]. +#[macro_export] +macro_rules! test { + ($f:ident, $req:expr) => { + $crate::multitest!(vec![Box::new($crate::SimpleTest::new( + stringify!($f), + $req, + $f + ))]); + }; +} + +/// Defines a set of tests from a [`Vec>`]. +#[macro_export] +macro_rules! multitest { + ($tests:expr) => { + const _: () = { + use $crate::test_macro_support::linkme; + // UNSAFETY: linkme uses manual link sections, which are unsafe. + #[expect(unsafe_code)] + #[linkme::distributed_slice($crate::test_macro_support::TESTS)] + #[linkme(crate = linkme)] + static TEST: fn() -> (&'static str, Vec>) = + || (module_path!(), $tests); + }; + }; +} + +/// A single test. +struct Test { + module: &'static str, + test: Box, +} + +impl Test { + /// Returns all the tests defined in this crate. + fn all() -> impl Iterator { + TESTS.iter().flat_map(|f| { + let (module, tests) = f(); + tests.into_iter().map(move |test| Self { module, test }) + }) + } + + /// Returns the name of the test. + fn name(&self) -> String { + // Strip the crate name from the module path, for consistency with libtest. + let module = self + .module + .split_once("::") + .map_or(self.module, |(_, rest)| rest); + format!("{}::{}", module, self.test.leaf_name()) + } + + /// Returns the artifact requirements for the test. + fn requirements(&self) -> TestArtifactRequirements { + // All tests require the log directory. + self.test + .requirements() + .require(petri_artifacts_common::artifacts::TEST_LOG_DIRECTORY) + } + + /// Returns a libtest-mimic trial to run the test. + fn trial( + self, + resolve: fn(&str, TestArtifactRequirements) -> anyhow::Result, + ) -> libtest_mimic::Trial { + libtest_mimic::Trial::test(self.name(), move || { + let name = self.name(); + let artifacts = resolve(&name, self.requirements()) + .map_err(|err| format!("failed to resolve artifacts: {:#}", err))?; + self.test.run(&name, &artifacts) + }) + } +} + +/// A test that can be run. +/// +/// Register it to be run with [`test!`] or [`multitest!`]. +pub trait RunTest: Send { + /// The leaf name of the test. + /// + /// To produce the full test name, this will be prefixed with the module + /// name where the test is defined. + fn leaf_name(&self) -> &str; + /// Returns the artifacts required by the test. + fn requirements(&self) -> TestArtifactRequirements; + /// Runs the test, which has been assigned `name`, with the given + /// `artifacts`. + fn run(&self, name: &str, artifacts: &TestArtifacts) -> Result<(), libtest_mimic::Failed>; +} + +/// A test defined by a fixed set of requirements and a run function. +pub struct SimpleTest { + leaf_name: &'static str, + requirements: TestArtifactRequirements, + run: F, +} + +impl SimpleTest +where + F: 'static + Send + Fn(&str, &TestArtifacts) -> Result<(), E>, + E: Into, +{ + /// Returns a new test with the given `leaf_name`, `requirements`, and `run` + /// functions. + pub fn new(leaf_name: &'static str, requirements: TestArtifactRequirements, run: F) -> Self { + SimpleTest { + leaf_name, + requirements, + run, + } + } +} + +impl RunTest for SimpleTest +where + F: 'static + Send + Fn(&str, &TestArtifacts) -> Result<(), E>, + E: Into, +{ + fn leaf_name(&self) -> &str { + self.leaf_name + } + + fn requirements(&self) -> TestArtifactRequirements { + self.requirements.clone() + } + + fn run(&self, name: &str, artifacts: &TestArtifacts) -> Result<(), libtest_mimic::Failed> { + (self.run)(name, artifacts).map_err(|err| format!("{:#}", err.into()))?; + Ok(()) + } +} + +#[derive(clap::Parser)] +struct Options { + /// Lists the required artifacts for all tests. + #[clap(long)] + list_required_artifacts: bool, + #[clap(flatten)] + inner: libtest_mimic::Arguments, +} + +/// Entry point for test binaries. +pub fn test_main( + resolve: fn(&str, TestArtifactRequirements) -> anyhow::Result, +) -> ! { + let mut args = ::parse(); + if args.list_required_artifacts { + // FUTURE: write this in a machine readable format. + for test in Test::all() { + let requirements = test.requirements(); + println!("{}:", test.name()); + for artifact in requirements.required_artifacts() { + println!("required: {artifact:?}"); + } + for artifact in requirements.optional_artifacts() { + println!("optional: {artifact:?}"); + } + println!(); + } + std::process::exit(0); + } + + // Always just use one thread to avoid interleaving logs and to avoid using + // too many resources. These tests are usually run under nextest, which will + // run them in parallel in separate processes with appropriate concurrency + // limits. + if !matches!(args.inner.test_threads, None | Some(1)) { + eprintln!("warning: ignoring value passed to --test-threads, using 1"); + } + args.inner.test_threads = Some(1); + + let trials = Test::all().map(|test| test.trial(resolve)).collect(); + libtest_mimic::run(&args.inner, trials).exit() +} diff --git a/petri/src/vm/hyperv/mod.rs b/petri/src/vm/hyperv/mod.rs index 6bbbaaa762..1b010a874d 100644 --- a/petri/src/vm/hyperv/mod.rs +++ b/petri/src/vm/hyperv/mod.rs @@ -47,8 +47,8 @@ pub struct PetriVmConfigHyperV { secure_boot_template: Option, openhcl_igvm: Option, - // Petri test dependency resolver - resolver: TestArtifacts, + // Petri test dependency artifacts + artifacts: TestArtifacts, driver: DefaultDriver, arch: MachineArch, @@ -107,12 +107,12 @@ impl PetriVm for PetriVmHyperV { impl PetriVmConfigHyperV { /// Create a new Hyper-V petri VM config pub fn new( + test_name: &str, firmware: Firmware, arch: MachineArch, - resolver: TestArtifacts, + artifacts: TestArtifacts, driver: &DefaultDriver, ) -> anyhow::Result { - let test_name = crate::get_test_name()?; let temp_dir = tempfile::tempdir()?; let (guest_state_isolation_type, generation, guest_artifact, igvm_artifact) = match &firmware { @@ -162,15 +162,15 @@ impl PetriVmConfigHyperV { // TODO: OpenHCL PCAT }; - let reference_disk_path = resolver.resolve(guest_artifact); - let openhcl_igvm = igvm_artifact.map(|a| resolver.resolve(a)); + let reference_disk_path = artifacts.get(guest_artifact); + let openhcl_igvm = igvm_artifact.map(|a| artifacts.get(a).to_owned()); Ok(PetriVmConfigHyperV { - name: test_name, + name: test_name.to_owned(), generation, guest_state_isolation_type, memory: 0x1_0000_0000, - vhd_paths: vec![vec![reference_disk_path]], + vhd_paths: vec![vec![reference_disk_path.to_owned()]], secure_boot_template: matches!(generation, powershell::HyperVGeneration::Two) .then_some(match firmware.os_flavor() { OsFlavor::Windows => powershell::HyperVSecureBootTemplate::MicrosoftWindows, @@ -182,7 +182,7 @@ impl PetriVmConfigHyperV { } }), openhcl_igvm, - resolver, + artifacts, driver: driver.clone(), arch, os_flavor: firmware.os_flavor(), @@ -253,7 +253,7 @@ impl PetriVmConfigHyperV { // Construct the agent disk. let agent_disk_path = self.temp_dir.path().join("cidata.vhd"); { - let agent_disk = build_agent_image(self.arch, self.os_flavor, &self.resolver) + let agent_disk = build_agent_image(self.arch, self.os_flavor, &self.artifacts) .context("failed to build agent image")?; disk_vhd1::Vhd1Disk::make_fixed(agent_disk.as_file()) .context("failed to make vhd for agent image")?; diff --git a/petri/src/vm/mod.rs b/petri/src/vm/mod.rs index 55a6b1dcce..f4a631c39e 100644 --- a/petri/src/vm/mod.rs +++ b/petri/src/vm/mod.rs @@ -7,7 +7,6 @@ pub mod hyperv; /// OpenVMM VM management pub mod openvmm; -use anyhow::Context; use async_trait::async_trait; use petri_artifacts_common::tags::GuestQuirks; use petri_artifacts_common::tags::MachineArch; @@ -300,21 +299,3 @@ pub struct OpenHclServicingFlags { /// Preserve DMA memory for NVMe devices if supported. pub enable_nvme_keepalive: bool, } - -/// Generates a name for the petri test based on the thread name -pub fn get_test_name() -> anyhow::Result { - // Use the current thread name for the test name, both cargo-test and - // cargo-nextest set this. - // FUTURE: If we ever want to use petri outside a testing context this - // will need to be revisited. - let current_thread = std::thread::current(); - let test_name = current_thread.name().context("no thread name configured")?; - if test_name.is_empty() { - anyhow::bail!("thread name is empty"); - } - if test_name == "main" { - anyhow::bail!("thread name is 'main', not running from test thread"); - } - // Windows paths can't include colons, replace them. - Ok(test_name.replace("::", "__")) -} diff --git a/petri/src/vm/openvmm/construct.rs b/petri/src/vm/openvmm/construct.rs index f901e57cc3..cc5b96edce 100644 --- a/petri/src/vm/openvmm/construct.rs +++ b/petri/src/vm/openvmm/construct.rs @@ -96,16 +96,13 @@ impl PetriVmConfigOpenVmm { pub fn new( firmware: Firmware, arch: MachineArch, - resolver: TestArtifacts, + artifacts: TestArtifacts, driver: &DefaultDriver, ) -> anyhow::Result { - let test_name = crate::get_test_name()?; - let setup = PetriVmConfigSetupCore { - test_name: &test_name, arch, firmware: &firmware, - resolver: &resolver, + artifacts: &artifacts, driver, }; @@ -384,7 +381,7 @@ impl PetriVmConfigOpenVmm { openhcl_diag_handler, linux_direct_serial_agent, driver: driver.clone(), - resolver, + artifacts, output_dir, _vsock_temp_paths: vsock_temp_paths, }, @@ -399,10 +396,9 @@ impl PetriVmConfigOpenVmm { } struct PetriVmConfigSetupCore<'a> { - test_name: &'a str, arch: MachineArch, firmware: &'a Firmware, - resolver: &'a TestArtifacts, + artifacts: &'a TestArtifacts, driver: &'a DefaultDriver, } @@ -442,12 +438,11 @@ impl PetriVmConfigSetupCore<'_> { fn create_log_files(&self) -> anyhow::Result { // DEVNOTE: This function runs before tracing is set up. - let test_log_dir = self.resolver.resolve(common_artifacts::TEST_LOG_DIRECTORY); - let output_dir = test_log_dir.join(self.test_name); + let output_dir = self.artifacts.get(common_artifacts::TEST_LOG_DIRECTORY); if output_dir.exists() { - std::fs::remove_dir_all(&output_dir)?; + std::fs::remove_dir_all(output_dir)?; } - std::fs::create_dir_all(&output_dir)?; + std::fs::create_dir_all(output_dir)?; // NOTE: Due to a WSL + Windows defender bug, .txt extensions take forever to create within WSL // when cross compiling. Name them .log which works around it. @@ -468,7 +463,7 @@ impl PetriVmConfigSetupCore<'_> { } Ok(TestLogFiles { - output_dir, + output_dir: output_dir.to_owned(), hvlite_file, guest_file, petri_file, @@ -610,14 +605,14 @@ impl PetriVmConfigSetupCore<'_> { Ok(match (self.arch, &self.firmware) { (MachineArch::X86_64, Firmware::LinuxDirect { .. }) => { let kernel = File::open( - self.resolver - .resolve(hvlite_artifacts::loadable::LINUX_DIRECT_TEST_KERNEL_X64), + self.artifacts + .get(hvlite_artifacts::loadable::LINUX_DIRECT_TEST_KERNEL_X64), ) .context("Failed to open kernel")? .into(); let initrd = File::open( - self.resolver - .resolve(hvlite_artifacts::loadable::LINUX_DIRECT_TEST_INITRD_X64), + self.artifacts + .get(hvlite_artifacts::loadable::LINUX_DIRECT_TEST_INITRD_X64), ) .context("Failed to open initrd")? .into(); @@ -631,14 +626,14 @@ impl PetriVmConfigSetupCore<'_> { } (MachineArch::Aarch64, Firmware::LinuxDirect { .. }) => { let kernel = File::open( - self.resolver - .resolve(hvlite_artifacts::loadable::LINUX_DIRECT_TEST_KERNEL_AARCH64), + self.artifacts + .get(hvlite_artifacts::loadable::LINUX_DIRECT_TEST_KERNEL_AARCH64), ) .context("Failed to open kernel")? .into(); let initrd = File::open( - self.resolver - .resolve(hvlite_artifacts::loadable::LINUX_DIRECT_TEST_INITRD_AARCH64), + self.artifacts + .get(hvlite_artifacts::loadable::LINUX_DIRECT_TEST_INITRD_AARCH64), ) .context("Failed to open initrd")? .into(); @@ -652,9 +647,8 @@ impl PetriVmConfigSetupCore<'_> { } (MachineArch::X86_64, Firmware::Pcat { .. }) => { let firmware = hvlite_pcat_locator::find_pcat_bios( - self.resolver - .try_resolve(hvlite_artifacts::loadable::PCAT_FIRMWARE_X64) - .as_deref(), + self.artifacts + .try_get(hvlite_artifacts::loadable::PCAT_FIRMWARE_X64), ) .context("Failed to load packaged PCAT binary")?; LoadMode::Pcat { @@ -663,7 +657,7 @@ impl PetriVmConfigSetupCore<'_> { } } (_, Firmware::Uefi { .. }) => { - let firmware = File::open(self.resolver.resolve(match self.arch { + let firmware = File::open(self.artifacts.get(match self.arch { MachineArch::X86_64 => hvlite_artifacts::loadable::UEFI_FIRMWARE_X64.erase(), MachineArch::Aarch64 => { hvlite_artifacts::loadable::UEFI_FIRMWARE_AARCH64.erase() @@ -709,7 +703,7 @@ impl PetriVmConfigSetupCore<'_> { false, ), }; - let path = self.resolver.resolve(igvm_artifact); + let path = self.artifacts.get(igvm_artifact); let file = File::open(path) .context("failed to open openhcl firmware file")? .into(); @@ -756,8 +750,8 @@ impl PetriVmConfigSetupCore<'_> { // Nothing to do, no guest } Firmware::Pcat { guest } => { - let path = self.resolver.resolve(guest.artifact()); - let inner_disk = open_disk_type(&path, true)?; + let path = self.artifacts.get(guest.artifact()); + let inner_disk = open_disk_type(path, true)?; let guest_media = match guest { PcatGuest::Vhd(_) => GuestMedia::Disk { read_only: false, @@ -792,7 +786,7 @@ impl PetriVmConfigSetupCore<'_> { vtl2_nvme_boot: false, .. } => { - let path = self.resolver.resolve(guest.artifact()); + let path = self.artifacts.get(guest.artifact()); devices.extend([Device::Vmbus( DeviceVtl::Vtl0, ScsiControllerHandle { @@ -811,7 +805,7 @@ impl PetriVmConfigSetupCore<'_> { disk: LayeredDiskHandle { layers: vec![ RamDiskLayerHandle { len: None }.into_resource().into(), - DiskLayerHandle(open_disk_type(&path, true)?) + DiskLayerHandle(open_disk_type(path, true)?) .into_resource() .into(), ], @@ -830,7 +824,7 @@ impl PetriVmConfigSetupCore<'_> { vtl2_nvme_boot: true, .. } => { - let path = self.resolver.resolve(guest.artifact()); + let path = self.artifacts.get(guest.artifact()); devices.extend([Device::Vpci(VpciDeviceConfig { vtl: DeviceVtl::Vtl2, instance_id: BOOT_NVME_INSTANCE, @@ -843,7 +837,7 @@ impl PetriVmConfigSetupCore<'_> { disk: LayeredDiskHandle { layers: vec![ RamDiskLayerHandle { len: None }.into_resource().into(), - DiskLayerHandle(open_disk_type(&path, true)?) + DiskLayerHandle(open_disk_type(path, true)?) .into_resource() .into(), ], @@ -927,8 +921,9 @@ impl PetriVmConfigSetupCore<'_> { let (crash, task) = spawn_dump_handler( self.driver, - self.resolver - .resolve(hvlite_artifacts::OPENHCL_DUMP_DIRECTORY), + self.artifacts + .get(hvlite_artifacts::OPENHCL_DUMP_DIRECTORY) + .to_owned(), None, ); task.detach(); @@ -980,9 +975,8 @@ impl PetriVmConfigSetupCore<'_> { let video_dev = match self.firmware { Firmware::Pcat { .. } => Some(VideoDevice::Vga( hvlite_pcat_locator::find_svga_bios( - self.resolver - .try_resolve(hvlite_artifacts::loadable::SVGA_FIRMWARE_X64) - .as_deref(), + self.artifacts + .try_get(hvlite_artifacts::loadable::SVGA_FIRMWARE_X64), ) .context("Failed to load VGA BIOS")?, )), diff --git a/petri/src/vm/openvmm/mod.rs b/petri/src/vm/openvmm/mod.rs index 49d3e031c2..73634004d6 100644 --- a/petri/src/vm/openvmm/mod.rs +++ b/petri/src/vm/openvmm/mod.rs @@ -105,7 +105,7 @@ struct PetriVmResourcesOpenVmm { // Externally injected management stuff also needed at runtime. driver: DefaultDriver, - resolver: TestArtifacts, + artifacts: TestArtifacts, output_dir: PathBuf, // Resources that are only kept so they can be dropped at the end. diff --git a/petri/src/vm/openvmm/modify.rs b/petri/src/vm/openvmm/modify.rs index e6d411d7bd..bca2deb363 100644 --- a/petri/src/vm/openvmm/modify.rs +++ b/petri/src/vm/openvmm/modify.rs @@ -161,7 +161,7 @@ impl PetriVmConfigOpenVmm { let LoadMode::Igvm { file, .. } = &mut self.config.load_mode else { panic!("Custom OpenHCL is only supported for OpenHCL firmware.") }; - *file = File::open(self.resources.resolver.resolve(artifact)) + *file = File::open(self.resources.artifacts.get(artifact)) .expect("Failed to open custom OpenHCL file") .into(); self diff --git a/petri/src/vm/openvmm/runtime.rs b/petri/src/vm/openvmm/runtime.rs index a6637f9bd5..5a5bc0eadc 100644 --- a/petri/src/vm/openvmm/runtime.rs +++ b/petri/src/vm/openvmm/runtime.rs @@ -107,9 +107,9 @@ impl PetriVmOpenVmm { self.inner.openhcl_diag().map(|x| &*x.vtl2_vsock_path) } - /// Get the artifact resolver constructed for this VM. - pub fn artifact_resolver(&self) -> &petri_artifacts_core::TestArtifacts { - &self.inner.resources.resolver + /// Get the artifacts for this VM. + pub fn artifacts(&self) -> &petri_artifacts_core::TestArtifacts { + &self.inner.resources.artifacts } /// Wait for the VM to halt, returning the reason for the halt. @@ -358,7 +358,7 @@ impl PetriVmInner { .as_ref() .context("openhcl not configured")?; - let igvm_path = self.resources.resolver.resolve(new_openhcl); + let igvm_path = self.resources.artifacts.get(new_openhcl); let igvm_file = fs_err::File::open(igvm_path).context("failed to open igvm file")?; self.worker .restart_openhcl(ged_send, flags, igvm_file.into()) diff --git a/petri/src/vm/openvmm/start.rs b/petri/src/vm/openvmm/start.rs index 192a57d08c..3e61038956 100644 --- a/petri/src/vm/openvmm/start.rs +++ b/petri/src/vm/openvmm/start.rs @@ -74,7 +74,7 @@ impl PetriVmConfigOpenVmm { let mesh = Mesh::new("petri_mesh".to_string())?; - let host = Self::hvlite_host(&mesh, &resources.resolver, hvlite_log_file) + let host = Self::hvlite_host(&mesh, &resources.artifacts, hvlite_log_file) .await .context("failed to create host process")?; let (worker, halt_notif) = Worker::launch(&host, config) @@ -144,7 +144,7 @@ impl PetriVmConfigOpenVmm { let agent_disk = build_agent_image( self.arch, self.firmware.os_flavor(), - &self.resources.resolver, + &self.resources.artifacts, ) .context("failed to build agent image")?; @@ -199,7 +199,7 @@ impl PetriVmConfigOpenVmm { Guid::from_static_str("766e96f8-2ceb-437e-afe3-a93169e48a7c"); let uh_agent_disk = - build_agent_image(self.arch, OsFlavor::Linux, &self.resources.resolver) + build_agent_image(self.arch, OsFlavor::Linux, &self.resources.artifacts) .context("failed to build agent image")?; self.config.vmbus_devices.push(( @@ -388,7 +388,7 @@ impl PetriVmConfigOpenVmm { let (host, runner) = mesh_worker::worker_host(); mesh.launch_host( ProcessConfig::new("vmm") - .process_name(resolver.resolve(hvlite_artifacts::OPENVMM_NATIVE)) + .process_name(resolver.get(hvlite_artifacts::OPENVMM_NATIVE)) .stderr(Some(stderr_write)), hvlite_defs::entrypoint::MeshHostParams { runner }, ) diff --git a/vmm_tests/petri_artifact_resolver_openvmm_known_paths/src/lib.rs b/vmm_tests/petri_artifact_resolver_openvmm_known_paths/src/lib.rs index 9890dee946..e23136dbd2 100644 --- a/vmm_tests/petri_artifact_resolver_openvmm_known_paths/src/lib.rs +++ b/vmm_tests/petri_artifact_resolver_openvmm_known_paths/src/lib.rs @@ -15,12 +15,19 @@ use tempfile::TempDir; use vmm_test_images::KnownIso; use vmm_test_images::KnownVhd; -/// An implementation of [`petri_artifacts_core::TestArtifactResolverBackend`] +/// An implementation of [`petri_artifacts_core::ResolveTestArtifact`] /// that resolves artifacts to various "known paths" within the context of /// the OpenVMM repository. -pub struct OpenvmmKnownPathsTestArtifactResolver; +pub struct OpenvmmKnownPathsTestArtifactResolver<'a>(&'a str); -impl petri_artifacts_core::TestArtifactResolverBackend for OpenvmmKnownPathsTestArtifactResolver { +impl<'a> OpenvmmKnownPathsTestArtifactResolver<'a> { + /// Creates a new resolver for a test with the given name. + pub fn new(test_name: &'a str) -> Self { + Self(test_name) + } +} + +impl petri_artifacts_core::ResolveTestArtifact for OpenvmmKnownPathsTestArtifactResolver<'_> { #[rustfmt::skip] fn resolve(&self, id: ErasedArtifactHandle) -> anyhow::Result { use petri_artifacts_common::artifacts as common; @@ -32,7 +39,7 @@ impl petri_artifacts_core::TestArtifactResolverBackend for OpenvmmKnownPathsTest _ if id == common::PIPETTE_WINDOWS_AARCH64 => pipette_path(MachineArch::Aarch64, PipetteFlavor::Windows), _ if id == common::PIPETTE_LINUX_AARCH64 => pipette_path(MachineArch::Aarch64, PipetteFlavor::Linux), - _ if id == common::TEST_LOG_DIRECTORY => test_log_directory_path(), + _ if id == common::TEST_LOG_DIRECTORY => test_log_directory_path(self.0), _ if id == OPENVMM_NATIVE => openvmm_native_executable_path(), _ if id == OPENHCL_DUMP_DIRECTORY => openhcl_dump_path(), @@ -348,13 +355,16 @@ fn openhcl_extras_path( ) } -/// Path to our standard test output directory. -fn test_log_directory_path() -> anyhow::Result { - Ok(if let Some(path) = std::env::var_os("TEST_OUTPUT_PATH") { +/// Path to the per-test test output directory. +fn test_log_directory_path(test_name: &str) -> anyhow::Result { + let root = if let Some(path) = std::env::var_os("TEST_OUTPUT_PATH") { PathBuf::from(path) } else { get_repo_root()?.join("vmm_test_results") - }) + }; + // Use a per-test subdirectory, replacing `::` with `__` to avoid issues + // with filesystems that don't support `::` in filenames. + Ok(root.join(test_name.replace("::", "__"))) } /// Path to the location for OpenHCL crash dump files. diff --git a/vmm_tests/vmm_test_macros/src/lib.rs b/vmm_tests/vmm_test_macros/src/lib.rs index b920def42c..12c0db9aaf 100644 --- a/vmm_tests/vmm_test_macros/src/lib.rs +++ b/vmm_tests/vmm_test_macros/src/lib.rs @@ -728,8 +728,8 @@ fn make_vmm_test(args: Args, item: ItemFn, specific_vmm: Option) -> syn::Re let original_args = match item.sig.inputs.len() { 1 => quote! {config}, - 2 => quote! {config, resolver}, - 3 => quote! {config, resolver, driver }, + 2 => quote! {config, artifacts}, + 3 => quote! {config, artifacts, driver }, _ => return Err(Error::new( item.sig.inputs.span(), "expected 1, 2, or 3 arguments (the PetriVmConfig, ArtifactResolver, and Driver)", @@ -739,9 +739,9 @@ fn make_vmm_test(args: Args, item: ItemFn, specific_vmm: Option) -> syn::Re let original_name = &item.sig.ident; let mut tests = TokenStream::new(); let mut guest_archs = HashSet::new(); + // FUTURE: compute all this in code instead of in the macro. for config in args.configs { let name = format!("{}_{original_name}", config.name_prefix(specific_vmm)); - let fn_name = Ident::new(&name, original_name.span()); let mut deps = config.deps(); let optional_deps = config.optional_deps(); @@ -762,9 +762,10 @@ fn make_vmm_test(args: Args, item: ItemFn, specific_vmm: Option) -> syn::Re | (None, Some(Vmm::HyperV)) => ( quote!(#[cfg(all(guest_arch=#guest_arch, windows))]), quote!(::petri::hyperv::PetriVmConfigHyperV::new( + test_name, #firmware, #arch, - resolver.clone(), + artifacts.clone(), &driver, )?), ), @@ -780,7 +781,7 @@ fn make_vmm_test(args: Args, item: ItemFn, specific_vmm: Option) -> syn::Re quote!(::petri::openvmm::PetriVmConfigOpenVmm::new( #firmware, #arch, - resolver.clone(), + artifacts.clone(), &driver, )?), ) @@ -793,26 +794,31 @@ fn make_vmm_test(args: Args, item: ItemFn, specific_vmm: Option) -> syn::Re petri_vm_config = quote!(Box::new(#petri_vm_config)); } - tests.extend(quote!( - #cfg_conditions - #[::pal_async::async_test] - async fn #fn_name(driver: ::pal_async::DefaultDriver) -> anyhow::Result<()> { - let resolver = crate::prelude::vmm_tests_artifact_resolver() - .require(::petri_artifacts_common::artifacts::TEST_LOG_DIRECTORY) - #( .require(#deps) )* - #( .require(#extra_deps) )* - #( .try_require(#optional_deps) )* - .finalize(); - let config = #petri_vm_config; - #original_name(#original_args).await - })) + let test = quote! { + #cfg_conditions + Box::new(::petri::SimpleTest::new( + #name, + ::petri::TestArtifactRequirements::new() + #( .require(#deps) )* + #( .require(#extra_deps) )* + #( .try_require(#optional_deps) )*, + |test_name, artifacts| { + ::pal_async::DefaultPool::run_with(|driver| async move { + let config = #petri_vm_config; + #original_name(#original_args).await + }) + } + )), + }; + + tests.extend(test); } let guest_archs = guest_archs.into_iter(); - // Allow dead code for tests that are not run on the current architecture. Ok(quote! { - #tests + ::petri::multitest!(vec![#tests]); + // Allow dead code for tests that are not run on the current architecture. #[cfg_attr(not(any(#(guest_arch = #guest_archs,)*)), allow(dead_code))] #item }) diff --git a/vmm_tests/vmm_test_petri_support/Cargo.toml b/vmm_tests/vmm_test_petri_support/Cargo.toml index 7d261c9255..99d665f460 100644 --- a/vmm_tests/vmm_test_petri_support/Cargo.toml +++ b/vmm_tests/vmm_test_petri_support/Cargo.toml @@ -12,8 +12,5 @@ petri_artifacts_vmm_test.workspace = true petri_artifacts_common.workspace = true petri.workspace = true -anyhow.workspace = true -parking_lot.workspace = true - [lints] workspace = true diff --git a/vmm_tests/vmm_test_petri_support/src/lib.rs b/vmm_tests/vmm_test_petri_support/src/lib.rs index d573587285..97b72391ca 100644 --- a/vmm_tests/vmm_test_petri_support/src/lib.rs +++ b/vmm_tests/vmm_test_petri_support/src/lib.rs @@ -13,40 +13,11 @@ use petri_artifacts_common::tags::MachineArch; use petri_artifacts_common::tags::OsFlavor; use petri_artifacts_vmm_test::artifacts as hvlite_artifacts; -/// See [`ListTestDepsArtifactResolver`](list_test_deps_resolver::ListTestDepsArtifactResolver) -pub mod list_test_deps_resolver { - use petri::ErasedArtifactHandle; - - /// POC of a "dry run" resolver that simply lists test dependencies, and - /// panics. - // FUTURE: Future test infra updates will update / introduce a new resolver - // that is able to emit structured test dependency manifests (JSON?) that - // tooling can ingest in order to auto-build/fetch dependencies. - #[derive(Default)] - pub struct ListTestDepsArtifactResolver { - artifacts: parking_lot::Mutex>, - } - - impl petri::TestArtifactResolverBackend for ListTestDepsArtifactResolver { - fn resolve(&self, id: ErasedArtifactHandle) -> anyhow::Result { - self.artifacts.lock().push(id); - Ok(std::path::PathBuf::new()) - } - - fn finalize(mut self: Box) { - for artifact in self.artifacts.get_mut() { - println!("{:?}", artifact) - } - panic!("done listing test artifacts") - } - } -} - /// Helper methods to streamline requesting common sets of dependencies via -/// [`petri::TestArtifactResolver`] -pub trait TestArtifactResolverExt { - /// Helper method to require standard HvLite test dependencies. - fn require_hvlite_standard(self, pipette_flavor: Option<(MachineArch, OsFlavor)>) -> Self; +/// [`petri::TestArtifactRequirements`] +pub trait TestArtifactRequirementsExt { + /// Helper method to require standard OpenVMM test dependencies. + fn require_openvmm_standard(self, pipette_flavor: Option<(MachineArch, OsFlavor)>) -> Self; /// Helper method to require standard OpenHCL test dependencies. fn require_openhcl_standard(self, openhcl_image: ArtifactHandle) -> Self @@ -54,9 +25,9 @@ pub trait TestArtifactResolverExt { A: IsOpenhclIgvm; } -impl TestArtifactResolverExt for petri::TestArtifactResolver { - /// Helper method to require standard HvLite test dependencies. - fn require_hvlite_standard(self, pipette_flavor: Option<(MachineArch, OsFlavor)>) -> Self { +impl TestArtifactRequirementsExt for petri::TestArtifactRequirements { + /// Helper method to require standard OpenVMM test dependencies. + fn require_openvmm_standard(self, pipette_flavor: Option<(MachineArch, OsFlavor)>) -> Self { let s = self .require(petri_artifacts_vmm_test::artifacts::OPENVMM_NATIVE) .require(petri_artifacts_common::artifacts::TEST_LOG_DIRECTORY); diff --git a/vmm_tests/vmm_tests/Cargo.toml b/vmm_tests/vmm_tests/Cargo.toml index 6de54d3f69..79709f2ef7 100644 --- a/vmm_tests/vmm_tests/Cargo.toml +++ b/vmm_tests/vmm_tests/Cargo.toml @@ -6,6 +6,10 @@ name = "vmm_tests" edition.workspace = true rust-version.workspace = true +[[test]] +name = "tests" +harness = false + [dev-dependencies] petri_artifact_resolver_openvmm_known_paths.workspace = true petri_artifacts_vmm_test.workspace = true diff --git a/vmm_tests/vmm_tests/tests/tests/main.rs b/vmm_tests/vmm_tests/tests/tests/main.rs index 304c8c5bdc..3080cb6219 100644 --- a/vmm_tests/vmm_tests/tests/tests/main.rs +++ b/vmm_tests/vmm_tests/tests/tests/main.rs @@ -24,24 +24,12 @@ mod x86_64; #[cfg(guest_arch = "x86_64")] mod x86_64_exclusive; -/// Common prelude shared by all VMM tests. -mod prelude { - /// Obtain a new [`petri::TestArtifactResolver`] - // DEVNOTE: this method is referenced by the `vmm_test` macro - // in order to let consuming crates easily configure what artifact resolver - // is being used. - // - // In order to change the name / signature of this method, you must also - // update the macro code! - pub fn vmm_tests_artifact_resolver() -> petri::TestArtifactResolver { - if std::env::var("VMM_TEST_LIST_TEST_DEPS").is_ok() { - petri::TestArtifactResolver::new(Box::new( - vmm_test_petri_support::list_test_deps_resolver::ListTestDepsArtifactResolver::default(), - )) - } else { - petri::TestArtifactResolver::new(Box::new( - petri_artifact_resolver_openvmm_known_paths::OpenvmmKnownPathsTestArtifactResolver, - )) - } - } +pub fn main() { + petri::test_main(|name, requirements| { + requirements.resolve( + petri_artifact_resolver_openvmm_known_paths::OpenvmmKnownPathsTestArtifactResolver::new( + name, + ), + ) + }) } diff --git a/vmm_tests/vmm_tests/tests/tests/ttrpc.rs b/vmm_tests/vmm_tests/tests/tests/ttrpc.rs index 286e0f02a2..c8d4b66128 100644 --- a/vmm_tests/vmm_tests/tests/tests/ttrpc.rs +++ b/vmm_tests/vmm_tests/tests/tests/ttrpc.rs @@ -5,37 +5,39 @@ #![cfg_attr(guest_arch = "aarch64", allow(unused_imports))] -use crate::prelude::*; use anyhow::Context; use guid::Guid; use hvlite_ttrpc_vmservice as vmservice; use pal_async::DefaultPool; +use petri::TestArtifactRequirements; use petri_artifacts_vmm_test::artifacts; use std::io::BufRead; use std::io::BufReader; use std::io::Read; use std::process::Stdio; use unix_socket::UnixStream; -use vmm_test_petri_support::TestArtifactResolverExt; +use vmm_test_petri_support::TestArtifactRequirementsExt; #[cfg(guest_arch = "x86_64")] -#[test] -fn test_ttrpc_interface() -> anyhow::Result<()> { - // This test doesn't use a Petri VM, so it needs to initialize tracing itself. - test_with_tracing::init(); - - let artifacts = vmm_tests_artifact_resolver() - .require_hvlite_standard(None) +petri::test!( + test_ttrpc_interface, + TestArtifactRequirements::new() + .require_openvmm_standard(None) .require(artifacts::loadable::LINUX_DIRECT_TEST_KERNEL_X64) .require(artifacts::loadable::LINUX_DIRECT_TEST_INITRD_X64) - .finalize(); +); + +#[cfg(guest_arch = "x86_64")] +fn test_ttrpc_interface(_name: &str, artifacts: &petri::TestArtifacts) -> anyhow::Result<()> { + // This test doesn't use a Petri VM, so it needs to initialize tracing itself. + test_with_tracing::init(); let mut socket_path = std::env::temp_dir(); socket_path.push(Guid::new_random().to_string()); tracing::info!(socket_path = %socket_path.display(), "launching hvlite with ttrpc"); - let mut child = std::process::Command::new(artifacts.resolve(artifacts::OPENVMM_NATIVE)) + let mut child = std::process::Command::new(artifacts.get(artifacts::OPENVMM_NATIVE)) .arg("--ttrpc") .arg(&socket_path) .stdin(Stdio::null()) @@ -58,8 +60,8 @@ fn test_ttrpc_interface() -> anyhow::Result<()> { } }); - let kernel_path = artifacts.resolve(artifacts::loadable::LINUX_DIRECT_TEST_KERNEL_X64); - let initrd_path = artifacts.resolve(artifacts::loadable::LINUX_DIRECT_TEST_INITRD_X64); + let kernel_path = artifacts.get(artifacts::loadable::LINUX_DIRECT_TEST_KERNEL_X64); + let initrd_path = artifacts.get(artifacts::loadable::LINUX_DIRECT_TEST_INITRD_X64); let ttrpc_path = socket_path.clone(); DefaultPool::run_with(|driver| async move {