From 4cb832558a324d2586183567b3b93e854a6a7a45 Mon Sep 17 00:00:00 2001 From: krishnakumar4a4 Date: Tue, 14 Jul 2026 23:20:26 +0530 Subject: [PATCH 01/12] refactor: decouple BuildAction from tasks using mockable runners Introduce BuildTaskRunner and PackageTaskRunner with #[automock] to decouple BuildAction orchestration tests from the full provider stack. BuildAction tests now mock runners instead of every individual provider, reducing tests.rs from ~3400 lines to ~700 lines while maintaining equivalent coverage through dedicated unit tests in build_task.rs and package_task.rs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: fa978dd1-9d61-4cea-a03a-7ebc914228fa --- .../cargo-wdk/src/actions/build/build_task.rs | 20 + crates/cargo-wdk/src/actions/build/mod.rs | 49 +- .../src/actions/build/package_task.rs | 863 +++- crates/cargo-wdk/src/actions/build/tests.rs | 3796 ++++------------- 4 files changed, 1731 insertions(+), 2997 deletions(-) diff --git a/crates/cargo-wdk/src/actions/build/build_task.rs b/crates/cargo-wdk/src/actions/build/build_task.rs index 4540166ad..543b3d5e3 100644 --- a/crates/cargo-wdk/src/actions/build/build_task.rs +++ b/crates/cargo-wdk/src/actions/build/build_task.rs @@ -10,6 +10,7 @@ use std::path::{Path, PathBuf}; use anyhow::Result; use cargo_metadata::Message; use clap_cargo::Features; +use mockall::automock; use mockall_double::double; use tracing::debug; use wdk_build::CpuArchitecture; @@ -24,6 +25,7 @@ use crate::{ }; /// Parameters for constructing a [`BuildTask`]. +#[derive(Clone, Copy)] pub struct BuildTaskParams<'a> { /// The name of the package to build pub package_name: &'a str, @@ -41,6 +43,24 @@ pub struct BuildTaskParams<'a> { pub verbosity_level: clap_verbosity_flag::Verbosity, } +#[derive(Debug, Default)] +#[allow(dead_code)] +pub struct BuildTaskRunner {} + +#[automock] +#[allow(dead_code, clippy::unused_self, clippy::elidable_lifetime_names)] +impl BuildTaskRunner { + pub fn run<'a>( + &self, + params: &BuildTaskParams<'a>, + command_exec: &CommandExec, + ) -> Result>, BuildTaskError> { + BuildTask::new(*params, command_exec) + .run() + .map(Iterator::collect) + } +} + /// Builds specified package by running `cargo build` pub struct BuildTask<'a> { params: BuildTaskParams<'a>, diff --git a/crates/cargo-wdk/src/actions/build/mod.rs b/crates/cargo-wdk/src/actions/build/mod.rs index 94ba1d017..538f8208a 100644 --- a/crates/cargo-wdk/src/actions/build/mod.rs +++ b/crates/cargo-wdk/src/actions/build/mod.rs @@ -18,13 +18,17 @@ use std::{ }; use anyhow::Result; -use build_task::{BuildTask, BuildTaskParams}; +use build_task::BuildTaskParams; +#[double] +use build_task::BuildTaskRunner; use cargo_metadata::{CrateType, Message, Metadata as CargoMetadata, Package, TargetKind}; use clap_cargo::Features; use error::BuildActionError; use mockall_double::double; +use package_task::PackageTaskParams; +#[double] +use package_task::PackageTaskRunner; pub use package_task::SignMode; -use package_task::{PackageTask, PackageTaskParams}; use tracing::{debug, error as err, info, trace, warn}; use wdk_build::{ CpuArchitecture, @@ -63,6 +67,8 @@ pub struct BuildAction<'a> { command_exec: &'a CommandExec, fs: &'a Fs, metadata: &'a Metadata, + build_task_runner: BuildTaskRunner, + package_task_runner: PackageTaskRunner, } impl<'a> BuildAction<'a> { @@ -89,6 +95,26 @@ impl<'a> BuildAction<'a> { command_exec: &'a CommandExec, fs: &'a Fs, metadata: &'a Metadata, + ) -> Result { + Self::new_with_runners( + params, + wdk_build, + command_exec, + fs, + metadata, + BuildTaskRunner::default(), + PackageTaskRunner::default(), + ) + } + + fn new_with_runners( + params: &BuildActionParams<'a>, + wdk_build: &'a WdkBuild, + command_exec: &'a CommandExec, + fs: &'a Fs, + metadata: &'a Metadata, + build_task_runner: BuildTaskRunner, + package_task_runner: PackageTaskRunner, ) -> Result { // TODO: validate params anyhow::ensure!( @@ -108,6 +134,8 @@ impl<'a> BuildAction<'a> { command_exec, fs, metadata, + build_task_runner, + package_task_runner, }) } @@ -345,8 +373,8 @@ impl<'a> BuildAction<'a> { let package_name = package.name.as_str(); info!("Building package {package_name}"); - let build_task = BuildTask::new( - BuildTaskParams { + let output_message_iter = self.build_task_runner.run( + &BuildTaskParams { package_name, working_dir, profile: self.profile, @@ -356,8 +384,7 @@ impl<'a> BuildAction<'a> { verbosity_level: self.verbosity_level, }, self.command_exec, - ); - let output_message_iter = build_task.run()?; + )?; let wdk_metadata = if let Ok(wdk_metadata) = wdk_metadata { debug!("Found wdk metadata in package: {}", package_name); @@ -391,15 +418,16 @@ impl<'a> BuildAction<'a> { self.get_target_arch_from_cargo_rustc(working_dir)? }; debug!("Target architecture for package: {package_name} is: {target_arch}"); - let target_dir = Self::get_target_dir_from_output(package, output_message_iter)?; + let target_dir = + Self::get_target_dir_from_output(package, output_message_iter.into_iter())?; debug!( "Target directory for package: {} is: {}", package_name, target_dir.display() ); - PackageTask::new( - PackageTaskParams { + self.package_task_runner.run( + &PackageTaskParams { package_name, working_dir, target_dir: &target_dir, @@ -411,8 +439,7 @@ impl<'a> BuildAction<'a> { self.wdk_build, self.command_exec, self.fs, - ) - .run()?; + )?; info!("Finished building {package_name}"); Ok(()) diff --git a/crates/cargo-wdk/src/actions/build/package_task.rs b/crates/cargo-wdk/src/actions/build/package_task.rs index 7bd2f3299..943ee7173 100644 --- a/crates/cargo-wdk/src/actions/build/package_task.rs +++ b/crates/cargo-wdk/src/actions/build/package_task.rs @@ -15,6 +15,7 @@ use std::{ result::Result, }; +use mockall::automock; use mockall_double::double; use tracing::{debug, info, warn}; use wdk_build::{CpuArchitecture, DriverConfig}; @@ -61,6 +62,38 @@ pub struct PackageTaskParams<'a> { pub driver_model: DriverConfig, } +#[derive(Debug, Default)] +#[allow(dead_code)] +pub struct PackageTaskRunner {} + +#[automock] +#[allow(dead_code, clippy::unused_self, clippy::elidable_lifetime_names)] +impl PackageTaskRunner { + pub fn run<'a>( + &self, + params: &PackageTaskParams<'a>, + wdk_build: &WdkBuild, + command_exec: &CommandExec, + fs: &Fs, + ) -> Result<(), PackageTaskError> { + PackageTask::new( + PackageTaskParams { + package_name: params.package_name, + working_dir: params.working_dir, + target_dir: params.target_dir, + target_arch: params.target_arch, + sign_mode: params.sign_mode, + sample_class: params.sample_class, + driver_model: params.driver_model.clone(), + }, + wdk_build, + command_exec, + fs, + ) + .run() + } +} + /// Supports low level driver packaging operations pub struct PackageTask<'a> { package_name: String, @@ -638,15 +671,24 @@ impl Drop for NamedMutex { } #[cfg(test)] +#[allow(clippy::needless_pass_by_value)] mod tests { use std::{ + os::windows::process::ExitStatusExt, path::PathBuf, process::{ExitStatus, Output}, }; - use wdk_build::{CpuArchitecture, KmdfConfig}; + use mockall::predicate::eq; + use wdk_build::{CpuArchitecture, DriverConfig, KmdfConfig}; use super::*; + use crate::providers::{ + error::{CommandError, FileError}, + exec::MockCommandExec, + fs::MockFs, + wdk_build::MockWdkBuild, + }; #[test] fn new_succeeds_for_valid_args() { @@ -772,6 +814,299 @@ mod tests { PackageTask::new(package_task_params, &wdk_build, &command_exec, &fs); } + #[test] + fn run_packages_driver_with_expected_operations() { + let mut harness = PackageTaskHarness::new(); + let paths = harness.paths(); + + harness + .expect_exists(paths.src_inx_file_path.clone(), true) + .expect_exists(paths.dest_root_package_folder.clone(), false) + .expect_create_dir_ok(paths.dest_root_package_folder.clone()) + .expect_rename( + paths.src_driver_binary_file_path, + paths.src_renamed_driver_binary_file_path.clone(), + Ok(()), + ) + .expect_copy( + paths.src_renamed_driver_binary_file_path, + paths.dest_driver_binary_path.clone(), + Ok(1), + ) + .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) + .expect_copy( + paths.src_inx_file_path, + paths.dest_inf_file_path.clone(), + Ok(1), + ) + .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) + .expect_stampinf( + paths.dest_inf_file_path.clone(), + CpuArchitecture::Amd64, + Some(String::from("1.33")), + ) + .expect_inf2cat(paths.dest_root_package_folder, "10_x64") + .expect_exists(paths.src_cert_file_path.clone(), false) + .expect_certmgr_exists_check(false) + .expect_certmgr_exists_check(false) + .expect_makecert(paths.src_cert_file_path.clone()) + .expect_copy(paths.src_cert_file_path, paths.dest_cert_file_path, Ok(1)) + .expect_signtool_sign(paths.dest_driver_binary_path) + .expect_signtool_sign(paths.dest_cat_file_path) + .expect_infverif(paths.dest_inf_file_path, "/w", None); + + let task = harness.task(); + let result = task.run(); + + assert!( + result.is_ok(), + "package task failed unexpectedly: {result:?}" + ); + } + + #[test] + fn run_verifies_signatures_when_enabled() { + let mut harness = PackageTaskHarness::new().with_sign_mode(SignMode::Test { + verify_signature: true, + }); + let paths = harness.paths(); + + harness + .expect_successful_packaging_without_verification() + .expect_signtool_verify(paths.dest_driver_binary_path) + .expect_signtool_verify(paths.dest_cat_file_path); + + let task = harness.task(); + let result = task.run(); + + assert!( + result.is_ok(), + "package task failed unexpectedly: {result:?}" + ); + } + + #[test] + fn run_exports_certificate_from_store_when_it_already_exists() { + let mut harness = PackageTaskHarness::new(); + let paths = harness.paths(); + + harness + .expect_exists(paths.src_inx_file_path.clone(), true) + .expect_exists(paths.dest_root_package_folder.clone(), true) + .expect_rename( + paths.src_driver_binary_file_path, + paths.src_renamed_driver_binary_file_path.clone(), + Ok(()), + ) + .expect_copy( + paths.src_renamed_driver_binary_file_path, + paths.dest_driver_binary_path.clone(), + Ok(1), + ) + .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) + .expect_copy( + paths.src_inx_file_path, + paths.dest_inf_file_path.clone(), + Ok(1), + ) + .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) + .expect_stampinf( + paths.dest_inf_file_path.clone(), + CpuArchitecture::Amd64, + Some(String::from("1.33")), + ) + .expect_inf2cat(paths.dest_root_package_folder, "10_x64") + .expect_exists(paths.src_cert_file_path.clone(), false) + .expect_certmgr_exists_check(true) + .expect_certmgr_create_cert_from_store(paths.src_cert_file_path.clone()) + .expect_copy(paths.src_cert_file_path, paths.dest_cert_file_path, Ok(1)) + .expect_signtool_sign(paths.dest_driver_binary_path) + .expect_signtool_sign(paths.dest_cat_file_path) + .expect_infverif(paths.dest_inf_file_path, "/w", None); + + let task = harness.task(); + let result = task.run(); + + assert!( + result.is_ok(), + "package task failed unexpectedly: {result:?}" + ); + } + + #[test] + fn run_returns_error_when_inx_file_is_missing() { + let mut harness = PackageTaskHarness::new(); + let paths = harness.paths(); + harness.expect_exists(paths.src_inx_file_path.clone(), false); + + let task = harness.task(); + let result = task.run(); + + assert!(matches!( + result, + Err(PackageTaskError::MissingInxSrcFile(path)) + if path == paths.src_inx_file_path + )); + } + + #[test] + fn run_returns_error_when_copying_driver_binary_fails() { + let mut harness = PackageTaskHarness::new(); + let paths = harness.paths(); + + harness + .expect_exists(paths.src_inx_file_path, true) + .expect_exists(paths.dest_root_package_folder, true) + .expect_rename( + paths.src_driver_binary_file_path, + paths.src_renamed_driver_binary_file_path.clone(), + Ok(()), + ) + .expect_copy( + paths.src_renamed_driver_binary_file_path.clone(), + paths.dest_driver_binary_path.clone(), + Err(FileError::CopyError( + paths.src_renamed_driver_binary_file_path.clone(), + paths.dest_driver_binary_path.clone(), + std::io::Error::new(std::io::ErrorKind::PermissionDenied, "copy failed"), + )), + ); + + let task = harness.task(); + let result = task.run(); + + assert!(matches!( + result, + Err(PackageTaskError::FileIo(FileError::CopyError(src, dest, _))) + if src == paths.src_renamed_driver_binary_file_path + && dest == paths.dest_driver_binary_path + )); + } + + #[test] + fn run_returns_error_when_stampinf_fails() { + let mut harness = PackageTaskHarness::new(); + let paths = harness.paths(); + + harness + .expect_exists(paths.src_inx_file_path.clone(), true) + .expect_exists(paths.dest_root_package_folder, true) + .expect_rename( + paths.src_driver_binary_file_path, + paths.src_renamed_driver_binary_file_path.clone(), + Ok(()), + ) + .expect_copy( + paths.src_renamed_driver_binary_file_path, + paths.dest_driver_binary_path, + Ok(1), + ) + .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) + .expect_copy( + paths.src_inx_file_path, + paths.dest_inf_file_path.clone(), + Ok(1), + ) + .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) + .expect_stampinf_error( + paths.dest_inf_file_path, + CpuArchitecture::Amd64, + Some(String::from("1.33")), + ); + + let task = harness.task(); + let result = task.run(); + + assert!(matches!(result, Err(PackageTaskError::StampinfCommand(_)))); + } + + #[test] + fn run_returns_error_when_inf2cat_fails() { + let mut harness = PackageTaskHarness::new(); + let paths = harness.paths(); + + harness + .expect_exists(paths.src_inx_file_path.clone(), true) + .expect_exists(paths.dest_root_package_folder.clone(), true) + .expect_rename( + paths.src_driver_binary_file_path, + paths.src_renamed_driver_binary_file_path.clone(), + Ok(()), + ) + .expect_copy( + paths.src_renamed_driver_binary_file_path, + paths.dest_driver_binary_path, + Ok(1), + ) + .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) + .expect_copy( + paths.src_inx_file_path, + paths.dest_inf_file_path.clone(), + Ok(1), + ) + .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) + .expect_stampinf( + paths.dest_inf_file_path, + CpuArchitecture::Amd64, + Some(String::from("1.33")), + ) + .expect_inf2cat_error(paths.dest_root_package_folder, "10_x64"); + + let task = harness.task(); + let result = task.run(); + + assert!(matches!(result, Err(PackageTaskError::Inf2CatCommand(_)))); + } + + #[test] + fn run_skips_infverif_for_samples_when_wdk_build_is_in_bugged_range() { + let mut harness = PackageTaskHarness::new().with_sample_class(true); + let paths = harness.paths(); + + harness + .expect_exists(paths.src_inx_file_path.clone(), true) + .expect_exists(paths.dest_root_package_folder.clone(), true) + .expect_rename( + paths.src_driver_binary_file_path, + paths.src_renamed_driver_binary_file_path.clone(), + Ok(()), + ) + .expect_copy( + paths.src_renamed_driver_binary_file_path, + paths.dest_driver_binary_path.clone(), + Ok(1), + ) + .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) + .expect_copy( + paths.src_inx_file_path, + paths.dest_inf_file_path.clone(), + Ok(1), + ) + .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) + .expect_stampinf( + paths.dest_inf_file_path, + CpuArchitecture::Amd64, + Some(String::from("1.33")), + ) + .expect_inf2cat(paths.dest_root_package_folder, "10_x64") + .expect_exists(paths.src_cert_file_path.clone(), false) + .expect_certmgr_exists_check(false) + .expect_certmgr_exists_check(false) + .expect_makecert(paths.src_cert_file_path.clone()) + .expect_copy(paths.src_cert_file_path, paths.dest_cert_file_path, Ok(1)) + .expect_signtool_sign(paths.dest_driver_binary_path) + .expect_signtool_sign(paths.dest_cat_file_path) + .expect_wdk_build_number(25798); + + let task = harness.task(); + let result = task.run(); + + assert!( + result.is_ok(), + "package task failed unexpectedly: {result:?}" + ); + } + #[test] fn stampinf_version_overrides_with_env_var() { // verify both with and without the env var set scenarios @@ -839,6 +1174,532 @@ mod tests { } } + struct PackageTaskHarness { + package_name: &'static str, + working_dir: PathBuf, + target_dir: PathBuf, + arch: CpuArchitecture, + sign_mode: SignMode, + sample_class: bool, + driver_model: DriverConfig, + command_exec: MockCommandExec, + wdk_build: MockWdkBuild, + fs: MockFs, + } + + impl PackageTaskHarness { + fn new() -> Self { + Self { + package_name: "sample-driver", + working_dir: PathBuf::from(r"C:\abs\sample-driver"), + target_dir: PathBuf::from(r"C:\abs\sample-driver\target\debug"), + arch: CpuArchitecture::Amd64, + sign_mode: SignMode::Test { + verify_signature: false, + }, + sample_class: false, + driver_model: DriverConfig::Kmdf(KmdfConfig { + kmdf_version_major: 1, + target_kmdf_version_minor: 33, + minimum_kmdf_version_minor: Some(33), + }), + command_exec: MockCommandExec::new(), + wdk_build: MockWdkBuild::new(), + fs: MockFs::new(), + } + } + + fn with_sign_mode(mut self, sign_mode: SignMode) -> Self { + self.sign_mode = sign_mode; + self + } + + fn with_sample_class(mut self, sample_class: bool) -> Self { + self.sample_class = sample_class; + self + } + + fn paths(&self) -> PackageTaskPaths { + PackageTaskPaths { + src_inx_file_path: self.src_inx_file_path(), + src_driver_binary_file_path: self.src_driver_binary_file_path(), + src_renamed_driver_binary_file_path: self.src_renamed_driver_binary_file_path(), + src_pdb_file_path: self.src_pdb_file_path(), + src_map_file_path: self.src_map_file_path(), + src_cert_file_path: self.src_cert_file_path(), + dest_root_package_folder: self.dest_root_package_folder(), + dest_inf_file_path: self.dest_inf_file_path(), + dest_driver_binary_path: self.dest_driver_binary_path(), + dest_pdb_file_path: self.dest_pdb_file_path(), + dest_map_file_path: self.dest_map_file_path(), + dest_cert_file_path: self.dest_cert_file_path(), + dest_cat_file_path: self.dest_cat_file_path(), + } + } + + fn task(&self) -> PackageTask<'_> { + PackageTask::new( + PackageTaskParams { + package_name: self.package_name, + working_dir: &self.working_dir, + target_dir: &self.target_dir, + target_arch: &self.arch, + sign_mode: self.sign_mode, + sample_class: self.sample_class, + driver_model: self.driver_model.clone(), + }, + &self.wdk_build, + &self.command_exec, + &self.fs, + ) + } + + fn normalized_package_name(&self) -> String { + self.package_name.replace('-', "_") + } + + fn src_inx_file_path(&self) -> PathBuf { + self.working_dir + .join(format!("{}.inx", self.normalized_package_name())) + } + + fn src_driver_binary_file_path(&self) -> PathBuf { + self.target_dir + .join(format!("{}.dll", self.normalized_package_name())) + } + + fn src_renamed_driver_binary_file_path(&self) -> PathBuf { + self.target_dir + .join(format!("{}.sys", self.normalized_package_name())) + } + + fn src_pdb_file_path(&self) -> PathBuf { + self.target_dir + .join(format!("{}.pdb", self.normalized_package_name())) + } + + fn src_map_file_path(&self) -> PathBuf { + self.target_dir + .join("deps") + .join(format!("{}.map", self.normalized_package_name())) + } + + fn src_cert_file_path(&self) -> PathBuf { + self.target_dir.join(format!("{WDR_LOCAL_TEST_CERT}.cer")) + } + + fn dest_root_package_folder(&self) -> PathBuf { + self.target_dir + .join(format!("{}_package", self.normalized_package_name())) + } + + fn dest_inf_file_path(&self) -> PathBuf { + self.dest_root_package_folder() + .join(format!("{}.inf", self.normalized_package_name())) + } + + fn dest_driver_binary_path(&self) -> PathBuf { + self.dest_root_package_folder() + .join(format!("{}.sys", self.normalized_package_name())) + } + + fn dest_pdb_file_path(&self) -> PathBuf { + self.dest_root_package_folder() + .join(format!("{}.pdb", self.normalized_package_name())) + } + + fn dest_map_file_path(&self) -> PathBuf { + self.dest_root_package_folder() + .join(format!("{}.map", self.normalized_package_name())) + } + + fn dest_cert_file_path(&self) -> PathBuf { + self.dest_root_package_folder() + .join(format!("{WDR_LOCAL_TEST_CERT}.cer")) + } + + fn dest_cat_file_path(&self) -> PathBuf { + self.dest_root_package_folder() + .join(format!("{}.cat", self.normalized_package_name())) + } + } + + #[derive(Clone)] + struct PackageTaskPaths { + src_inx_file_path: PathBuf, + src_driver_binary_file_path: PathBuf, + src_renamed_driver_binary_file_path: PathBuf, + src_pdb_file_path: PathBuf, + src_map_file_path: PathBuf, + src_cert_file_path: PathBuf, + dest_root_package_folder: PathBuf, + dest_inf_file_path: PathBuf, + dest_driver_binary_path: PathBuf, + dest_pdb_file_path: PathBuf, + dest_map_file_path: PathBuf, + dest_cert_file_path: PathBuf, + dest_cat_file_path: PathBuf, + } + + impl PackageTaskHarness { + fn expect_successful_packaging_without_verification(&mut self) -> &mut Self { + let paths = self.paths(); + self.expect_exists(paths.src_inx_file_path.clone(), true) + .expect_exists(paths.dest_root_package_folder.clone(), true) + .expect_rename( + paths.src_driver_binary_file_path, + paths.src_renamed_driver_binary_file_path.clone(), + Ok(()), + ) + .expect_copy( + paths.src_renamed_driver_binary_file_path, + paths.dest_driver_binary_path.clone(), + Ok(1), + ) + .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) + .expect_copy( + paths.src_inx_file_path, + paths.dest_inf_file_path.clone(), + Ok(1), + ) + .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) + .expect_stampinf( + paths.dest_inf_file_path.clone(), + CpuArchitecture::Amd64, + Some(String::from("1.33")), + ) + .expect_inf2cat(paths.dest_root_package_folder, "10_x64") + .expect_exists(paths.src_cert_file_path.clone(), true) + .expect_copy(paths.src_cert_file_path, paths.dest_cert_file_path, Ok(1)) + .expect_signtool_sign(paths.dest_driver_binary_path) + .expect_signtool_sign(paths.dest_cat_file_path) + .expect_infverif(paths.dest_inf_file_path, "/w", None) + } + + fn expect_exists(&mut self, path: PathBuf, exists: bool) -> &mut Self { + self.fs + .expect_exists() + .with(eq(path)) + .once() + .return_once(move |_| exists); + self + } + + fn expect_create_dir_ok(&mut self, path: PathBuf) -> &mut Self { + self.fs + .expect_create_dir() + .with(eq(path)) + .once() + .return_once(|_| Ok(())); + self + } + + fn expect_rename( + &mut self, + src: PathBuf, + dest: PathBuf, + result: Result<(), FileError>, + ) -> &mut Self { + self.fs + .expect_rename() + .with(eq(src), eq(dest)) + .once() + .return_once(move |_, _| result); + self + } + + fn expect_copy( + &mut self, + src: PathBuf, + dest: PathBuf, + result: Result, + ) -> &mut Self { + self.fs + .expect_copy() + .with(eq(src), eq(dest)) + .once() + .return_once(move |_, _| result); + self + } + + fn expect_stampinf( + &mut self, + dest_inf_file_path: PathBuf, + arch: CpuArchitecture, + wdf_version: Option, + ) -> &mut Self { + self.command_exec + .expect_run() + .withf(move |command, args, _, _| { + let dest_inf = dest_inf_file_path.to_string_lossy().to_string(); + command == "stampinf" + && args.len() >= 8 + && args[0] == "-f" + && args[1] == dest_inf + && args[2] == "-d" + && args[3] == "*" + && args[4] == "-a" + && args[5] == arch.to_string() + && args[6] == "-c" + && args[7] == "sample_driver.cat" + && args.windows(2).any(|window| window == ["-v", "*"]) + && wdf_version.as_deref().is_none_or(|version| { + args.windows(2).any(|window| window == ["-k", version]) + }) + }) + .once() + .return_once(|_, _, _, _| Ok(success_output())); + self + } + + fn expect_stampinf_error( + &mut self, + dest_inf_file_path: PathBuf, + arch: CpuArchitecture, + wdf_version: Option, + ) -> &mut Self { + self.command_exec + .expect_run() + .withf(move |command, args, _, _| { + let dest_inf = dest_inf_file_path.to_string_lossy().to_string(); + command == "stampinf" + && args.len() >= 8 + && args[0] == "-f" + && args[1] == dest_inf + && args[4] == "-a" + && args[5] == arch.to_string() + && wdf_version.as_deref().is_none_or(|version| { + args.windows(2).any(|window| window == ["-k", version]) + }) + }) + .once() + .return_once(|_, _, _, _| Err(command_error("stampinf"))); + self + } + + fn expect_inf2cat(&mut self, dest_root: PathBuf, os_mapping: &str) -> &mut Self { + let trimmed_dest_root = dest_root + .to_string_lossy() + .trim_start_matches("\\\\?\\") + .to_string(); + let expected_driver_arg = format!("/driver:{trimmed_dest_root}"); + let expected_os_arg = format!("/os:{os_mapping}"); + self.command_exec + .expect_run() + .withf(move |command, args, _, _| { + command == "inf2cat" + && args + == [ + expected_driver_arg.as_str(), + expected_os_arg.as_str(), + "/uselocaltime", + ] + }) + .once() + .return_once(|_, _, _, _| Ok(success_output())); + self + } + + fn expect_inf2cat_error(&mut self, dest_root: PathBuf, os_mapping: &str) -> &mut Self { + let trimmed_dest_root = dest_root + .to_string_lossy() + .trim_start_matches("\\\\?\\") + .to_string(); + let expected_driver_arg = format!("/driver:{trimmed_dest_root}"); + let expected_os_arg = format!("/os:{os_mapping}"); + self.command_exec + .expect_run() + .withf(move |command, args, _, _| { + command == "inf2cat" + && args + == [ + expected_driver_arg.as_str(), + expected_os_arg.as_str(), + "/uselocaltime", + ] + }) + .once() + .return_once(|_, _, _, _| Err(command_error("inf2cat"))); + self + } + + fn expect_certmgr_exists_check(&mut self, has_cert: bool) -> &mut Self { + let stdout = if has_cert { + r"==============Certificate # 1 ========== + Subject:: + [0,0] 2.5.4.3 (CN) WDRLocalTestCert + CertMgr Succeeded" + .as_bytes() + .to_vec() + } else { + r"==============No Certificates ========== + CertMgr Succeeded" + .as_bytes() + .to_vec() + }; + self.command_exec + .expect_run() + .withf(|command, args, _, _| { + command == "certmgr.exe" && args == ["-s", WDR_TEST_CERT_STORE] + }) + .once() + .return_once(move |_, _, _, _| { + Ok(Output { + status: ExitStatus::default(), + stdout, + stderr: vec![], + }) + }); + self + } + + fn expect_certmgr_create_cert_from_store(&mut self, cert_path: PathBuf) -> &mut Self { + let cert_path = cert_path.to_string_lossy().to_string(); + self.command_exec + .expect_run() + .withf(move |command, args, _, _| { + command == "certmgr.exe" + && args + == [ + "-put", + "-s", + WDR_TEST_CERT_STORE, + "-c", + "-n", + WDR_LOCAL_TEST_CERT, + cert_path.as_str(), + ] + }) + .once() + .return_once(|_, _, _, _| Ok(success_output())); + self + } + + fn expect_makecert(&mut self, cert_path: PathBuf) -> &mut Self { + let cert_path = cert_path.to_string_lossy().to_string(); + self.command_exec + .expect_run() + .withf(move |command, args, _, _| { + command == "makecert" + && args + == [ + "-r", + "-pe", + "-a", + "SHA256", + "-eku", + "1.3.6.1.5.5.7.3.3", + "-ss", + WDR_TEST_CERT_STORE, + "-n", + "CN=WDRLocalTestCert", + cert_path.as_str(), + ] + }) + .once() + .return_once(|_, _, _, _| Ok(success_output())); + self + } + + fn expect_signtool_sign(&mut self, file_path: PathBuf) -> &mut Self { + let file_path = file_path.to_string_lossy().to_string(); + self.command_exec + .expect_run() + .withf(move |command, args, _, _| { + command == "signtool" + && args + == [ + "sign", + "/v", + "/s", + WDR_TEST_CERT_STORE, + "/n", + WDR_LOCAL_TEST_CERT, + "/t", + "http://timestamp.digicert.com", + "/fd", + "SHA256", + file_path.as_str(), + ] + }) + .once() + .return_once(|_, _, _, _| Ok(success_output())); + self + } + + fn expect_signtool_verify(&mut self, file_path: PathBuf) -> &mut Self { + let file_path = file_path.to_string_lossy().to_string(); + self.command_exec + .expect_run() + .withf(move |command, args, _, _| { + command == "signtool" && args == ["verify", "/v", "/pa", file_path.as_str()] + }) + .once() + .return_once(|_, _, _, _| Ok(success_output())); + self + } + + fn expect_infverif( + &mut self, + inf_path: PathBuf, + driver_flag: &str, + sample_flag: Option<&'static str>, + ) -> &mut Self { + let inf_path = inf_path.to_string_lossy().to_string(); + let expected_args = if let Some(sample_flag) = sample_flag { + vec![ + "/v".to_string(), + driver_flag.to_string(), + sample_flag.to_string(), + inf_path, + ] + } else { + vec!["/v".to_string(), driver_flag.to_string(), inf_path] + }; + self.command_exec + .expect_run() + .withf(move |command, args, _, _| { + command == "infverif" + && args.len() == expected_args.len() + && args + .iter() + .zip(expected_args.iter()) + .all(|(actual, expected)| actual == expected) + }) + .once() + .return_once(|_, _, _, _| Ok(success_output())); + self + } + + fn expect_wdk_build_number(&mut self, build_number: u32) -> &mut Self { + self.wdk_build + .expect_detect_wdk_build_number() + .once() + .return_once(move || Ok(build_number)); + self + } + } + + fn success_output() -> Output { + Output { + status: ExitStatus::default(), + stdout: vec![], + stderr: vec![], + } + } + + fn command_error(command: &'static str) -> CommandError { + CommandError::from_output( + command, + &[], + &Output { + status: ExitStatus::from_raw(1), + stdout: vec![], + stderr: b"command failed".to_vec(), + }, + ) + } + mod named_mutex { use std::{ ffi::CString, diff --git a/crates/cargo-wdk/src/actions/build/tests.rs b/crates/cargo-wdk/src/actions/build/tests.rs index 882ffda26..36c1b8ef3 100644 --- a/crates/cargo-wdk/src/actions/build/tests.rs +++ b/crates/cargo-wdk/src/actions/build/tests.rs @@ -1,1755 +1,645 @@ // Copyright (c) Microsoft Corporation // License: MIT OR Apache-2.0 -#![allow(clippy::too_many_lines)] // Package tests are longer and splitting them into sub functions can make the code less readable -#![allow(clippy::ref_option_ref)] // This is suppressed for mockall as it generates mocks with env_vars: &Option +#![allow(clippy::too_many_lines)] +#![allow(clippy::ref_option_ref)] + use std::{ collections::HashMap, + fs, + io, os::windows::process::ExitStatusExt, path::{Path, PathBuf}, process::{ExitStatus, Output}, - result::Result::Ok, + sync::atomic::{AtomicU64, Ordering}, }; -use cargo_metadata::Metadata as CargoMetadata; +use cargo_metadata::{Message, Metadata as CargoMetadata}; use clap_cargo::Features; use mockall::predicate::eq; -use mockall_double::double; -use wdk_build::{ - CpuArchitecture, - DriverConfig, - metadata::{TryFromCargoMetadataError, Wdk}, -}; - -#[double] -use crate::providers::{ - exec::CommandExec, - fs::Fs, - metadata::Metadata as MetadataProvider, - wdk_build::WdkBuild, +use wdk_build::{CpuArchitecture, DriverConfig}; + +use super::{ + BuildAction, + BuildActionError, + BuildActionParams, + SignMode, + build_task::{BuildTaskParams, MockBuildTaskRunner}, + package_task::{MockPackageTaskRunner, PackageTaskParams}, }; use crate::{ - actions::{ - Profile, - build::{BuildAction, BuildActionParams, SignMode, error::BuildActionError}, - to_target_triple, + actions::{Profile, to_target_triple}, + providers::{ + error::{CommandError, FileError}, + exec::MockCommandExec, + fs::MockFs, + metadata::MockMetadata, + wdk_build::MockWdkBuild, }, - providers::error::{CommandError, FileError}, }; -//////////////////////////////////////////////////////////////////////////////// -/// Standalone driver project tests -//////////////////////////////////////////////////////////////////////////////// -// Test name is of form Given When Then -// Given: A driver project -// When: Default values are provided -// Then: It builds successfully +const DEFAULT_DRIVER_NAME: &str = "sample-driver"; +const DEFAULT_DRIVER_VERSION: &str = "0.0.1"; + +type PackageSpec<'a> = ( + &'a str, + PathBuf, + Option, + &'a str, + &'a str, + &'a str, +); + #[test] -pub fn given_a_driver_project_when_default_values_are_provided_then_it_builds_successfully() { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; - let target_arch = CpuArchitecture::Amd64; - let verify_signature = false; - let sample_class = false; - // Driver project data - let driver_type = "KMDF"; - let driver_name = "sample-kmdf"; - let driver_version = "0.0.1"; - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member, package) = - get_cargo_metadata_package(&cwd, driver_name, driver_version, Some(&wdk_metadata)); - - let cargo_build_output = - create_cargo_build_output_json(driver_name, driver_version, &cwd, None, profile); - - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) - .set_up_standalone_driver_project((workspace_member, package)) - .expect_default_build_task_steps(driver_name, Some(cargo_build_output)) - .expect_probe_target_arch_using_cargo_rustc(&cwd, target_arch, None) - .expect_default_package_task_steps(driver_name, "KMDF", target_arch, verify_signature); - - assert_build_action_run_with_env_is_success( - &cwd, - profile, +fn given_a_driver_project_when_target_arch_is_detected_then_build_action_orchestrates_build_and_package() + { + let cwd = PathBuf::from(r"C:\tmp\sample-driver"); + let target_dir = expected_target_dir(&cwd, None, None); + let mut test_build_action = TestBuildAction::new( + cwd.clone(), None, - verify_signature, - sample_class, - test_build_action, - ); -} + None, + SignMode::Test { + verify_signature: false, + }, + false, + ) + .set_up_standalone_driver_project(DEFAULT_DRIVER_NAME, Some(default_wdk_metadata())); -#[test] -pub fn given_a_driver_project_when_profile_is_release_then_it_builds_successfully() { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = Some(Profile::Release); - let target_arch = CpuArchitecture::Amd64; - let verify_signature = false; - let sample_class = false; - - // Driver project data - let driver_type = "KMDF"; - let driver_name = "sample-kmdf"; - let driver_version = "0.0.1"; - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member, package) = - get_cargo_metadata_package(&cwd, driver_name, driver_version, Some(&wdk_metadata)); - - let cargo_build_output = - create_cargo_build_output_json(driver_name, driver_version, &cwd, None, profile); - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) - .set_up_standalone_driver_project((workspace_member, package)) - .expect_default_build_task_steps(driver_name, Some(cargo_build_output)) - .expect_probe_target_arch_using_cargo_rustc(&cwd, target_arch, None) - .expect_default_package_task_steps(driver_name, "KMDF", target_arch, verify_signature); - - assert_build_action_run_with_env_is_success( + test_build_action.expect_build_runner( + DEFAULT_DRIVER_NAME, &cwd, - profile, None, - verify_signature, - sample_class, - test_build_action, + None, + Ok(cargo_build_messages( + DEFAULT_DRIVER_NAME, + DEFAULT_DRIVER_VERSION, + &cwd, + None, + None, + )), ); -} - -#[test] -pub fn given_a_driver_project_when_target_arch_is_arm64_then_it_builds_successfully() { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; - let target_arch = CpuArchitecture::Arm64; - let verify_signature = false; - let sample_class = false; - - // Driver project data - let driver_type = "KMDF"; - let driver_name = "sample-kmdf"; - let driver_version = "0.0.1"; - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member, package) = - get_cargo_metadata_package(&cwd, driver_name, driver_version, Some(&wdk_metadata)); - - let cargo_build_output = create_cargo_build_output_json( - driver_name, - driver_version, + test_build_action.expect_probe_target_arch_using_cargo_rustc(&cwd, CpuArchitecture::Amd64); + test_build_action.expect_package_runner( + DEFAULT_DRIVER_NAME, &cwd, - Some(&to_target_triple(target_arch)), - profile, + &target_dir, + CpuArchitecture::Amd64, + SignMode::Test { + verify_signature: false, + }, + false, ); - let test_build_action = - &TestBuildAction::new(cwd.clone(), profile, Some(target_arch), sample_class) - .set_up_standalone_driver_project((workspace_member, package)) - .expect_default_build_task_steps(driver_name, Some(cargo_build_output)) - .expect_default_package_task_steps(driver_name, "KMDF", target_arch, verify_signature); - assert_build_action_run_with_env_is_success( - &cwd, - profile, - Some(target_arch), - verify_signature, - sample_class, - test_build_action, + let build_action = initialize_build_action(&mut test_build_action); + let result = build_action.run_from_workspace_root(&cwd); + + assert!( + result.is_ok(), + "build action failed unexpectedly: {result:?}" ); } #[test] -pub fn given_a_driver_project_when_profile_is_release_and_target_arch_is_arm64_then_it_builds_successfully() - { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); +fn given_explicit_target_arch_when_building_then_probe_is_skipped_and_package_runner_uses_it() { + let cwd = PathBuf::from(r"C:\tmp\sample-driver"); let profile = Some(Profile::Release); let target_arch = CpuArchitecture::Arm64; - let verify_signature = false; - let sample_class = false; - - // Driver project data - let driver_type = "KMDF"; - let driver_name = "sample-kmdf"; - let driver_version = "0.0.1"; - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member, package) = - get_cargo_metadata_package(&cwd, driver_name, driver_version, Some(&wdk_metadata)); - - let cargo_build_output = create_cargo_build_output_json( - driver_name, - driver_version, - &cwd, - Some(&to_target_triple(target_arch)), + let target_dir = expected_target_dir(&cwd, Some(target_arch), profile); + let target_triple = to_target_triple(target_arch); + let mut test_build_action = TestBuildAction::new( + cwd.clone(), profile, - ); - - let test_build_action = - &TestBuildAction::new(cwd.clone(), profile, Some(target_arch), sample_class) - .set_up_standalone_driver_project((workspace_member, package)) - .expect_default_build_task_steps(driver_name, Some(cargo_build_output)) - .expect_default_package_task_steps(driver_name, "KMDF", target_arch, verify_signature); + Some(target_arch), + SignMode::Test { + verify_signature: true, + }, + true, + ) + .set_up_standalone_driver_project(DEFAULT_DRIVER_NAME, Some(default_wdk_metadata())); - assert_build_action_run_with_env_is_success( + test_build_action.expect_build_runner( + DEFAULT_DRIVER_NAME, &cwd, profile, Some(target_arch), - verify_signature, - sample_class, - test_build_action, + Ok(cargo_build_messages( + DEFAULT_DRIVER_NAME, + DEFAULT_DRIVER_VERSION, + &cwd, + Some(target_triple.as_str()), + profile, + )), ); -} - -#[test] -pub fn given_a_driver_project_when_sample_class_is_true_then_it_builds_successfully() { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; - let target_arch = CpuArchitecture::Amd64; - let verify_signature = false; - let sample_class = true; - - // Driver project data - let driver_type = "KMDF"; - let driver_name = "sample-kmdf"; - let driver_version = "0.0.1"; - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member, package) = - get_cargo_metadata_package(&cwd, driver_name, driver_version, Some(&wdk_metadata)); - - let cargo_build_output = - create_cargo_build_output_json(driver_name, driver_version, &cwd, None, profile); - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) - .set_up_standalone_driver_project((workspace_member, package)) - .expect_default_build_task_steps(driver_name, Some(cargo_build_output)) - .expect_probe_target_arch_using_cargo_rustc(&cwd, target_arch, None) - .expect_default_package_task_steps(driver_name, driver_type, target_arch, verify_signature) - .expect_detect_wdk_build_number(25100u32); - - assert_build_action_run_with_env_is_success( + test_build_action.expect_package_runner( + DEFAULT_DRIVER_NAME, &cwd, - profile, - None, - verify_signature, - sample_class, - test_build_action, + &target_dir, + target_arch, + SignMode::Test { + verify_signature: true, + }, + true, ); -} -#[test] -pub fn given_a_driver_project_when_verify_signature_is_true_then_it_builds_successfully() { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; - let target_arch = CpuArchitecture::Amd64; - let verify_signature = true; - let sample_class = false; - - // Driver project data - let driver_type = "KMDF"; - let driver_name = "sample-kmdf"; - let driver_version = "0.0.1"; - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member, package) = - get_cargo_metadata_package(&cwd, driver_name, driver_version, Some(&wdk_metadata)); - - let cargo_build_output = - create_cargo_build_output_json(driver_name, driver_version, &cwd, None, profile); - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) - .set_up_standalone_driver_project((workspace_member, package)) - .expect_default_build_task_steps(driver_name, Some(cargo_build_output)) - .expect_probe_target_arch_using_cargo_rustc(&cwd, target_arch, None) - .expect_default_package_task_steps(driver_name, driver_type, target_arch, verify_signature); - - assert_build_action_run_with_env_is_success( - &cwd, - profile, - None, - verify_signature, - sample_class, - test_build_action, + let build_action = initialize_build_action(&mut test_build_action); + let result = build_action.run_from_workspace_root(&cwd); + + assert!( + result.is_ok(), + "build action failed unexpectedly: {result:?}" ); } #[test] -pub fn given_a_driver_project_when_sign_mode_is_off_then_signing_and_verification_steps_are_skipped() - { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; - let target_arch = CpuArchitecture::Amd64; - let verify_signature = false; - let sample_class = false; - - // Driver project data - let driver_type = "KMDF"; - let driver_name = "sample-kmdf"; - let driver_version = "0.0.1"; - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member, package) = - get_cargo_metadata_package(&cwd, driver_name, driver_version, Some(&wdk_metadata)); - - let cargo_build_output = - create_cargo_build_output_json(driver_name, driver_version, &cwd, None, profile); - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) - .with_sign_mode(SignMode::Off) - .set_up_standalone_driver_project((workspace_member, package)) - .expect_default_build_task_steps(driver_name, Some(cargo_build_output)) - .expect_probe_target_arch_using_cargo_rustc(&cwd, target_arch, None) - .expect_package_task_steps_with_sign_mode_off(driver_name, driver_type, target_arch); - - assert_build_action_run_with_env_is_success( - &cwd, - profile, +fn given_a_non_driver_package_when_building_then_package_runner_is_skipped() { + let cwd = PathBuf::from(r"C:\tmp\non-driver"); + let mut test_build_action = TestBuildAction::new( + cwd.clone(), None, - verify_signature, - sample_class, - test_build_action, - ); -} + None, + SignMode::Test { + verify_signature: false, + }, + false, + ) + .set_up_standalone_driver_project(DEFAULT_DRIVER_NAME, None); -#[test] -pub fn given_a_driver_project_when_locked_is_set_then_it_is_forwarded_to_cargo_invocations() { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; - let target_arch = CpuArchitecture::Amd64; - let verify_signature = false; - let sample_class = false; - - // Driver project data - let driver_type = "KMDF"; - let driver_name = "sample-kmdf"; - let driver_version = "0.0.1"; - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member, package) = - get_cargo_metadata_package(&cwd, driver_name, driver_version, Some(&wdk_metadata)); - - let cargo_build_output = - create_cargo_build_output_json(driver_name, driver_version, &cwd, None, profile); - - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) - .with_locked(true) - .set_up_standalone_driver_project((workspace_member, package)) - .expect_default_build_task_steps(driver_name, Some(cargo_build_output)) - .expect_probe_target_arch_using_cargo_rustc(&cwd, target_arch, None) - .expect_default_package_task_steps(driver_name, driver_type, target_arch, verify_signature); - - assert_build_action_run_with_env_is_success( + test_build_action.expect_build_runner( + DEFAULT_DRIVER_NAME, &cwd, - profile, None, - verify_signature, - sample_class, - test_build_action, + None, + Ok(cargo_build_messages( + DEFAULT_DRIVER_NAME, + DEFAULT_DRIVER_VERSION, + &cwd, + None, + None, + )), ); -} - -#[test] -pub fn given_a_driver_project_when_self_signed_exists_then_it_should_skip_calling_makecert() { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; - let target_arch = CpuArchitecture::Amd64; - let verify_signature = true; - let sample_class = false; - - // Driver project data - let driver_type = "KMDF"; - let driver_name = "sample-kmdf"; - let driver_version = "0.0.1"; - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member, package) = - get_cargo_metadata_package(&cwd, driver_name, driver_version, Some(&wdk_metadata)); - let expected_certmgr_output = Output { - status: ExitStatus::default(), - stdout: r"==============Certificate # 1 ========== - Subject:: - [0,0] 2.5.4.3 (CN) WDRLocalTestCert - Issuer:: - [0,0] 2.5.4.3 (CN) WDRLocalTestCert - SerialNumber:: - 5E 04 0D 63 35 20 76 A5 4A E1 96 BF CF 01 0F 96 - SHA1 Thumbprint:: - FB972842 C63CD369 E07D0C71 88E17921 B5813C71 - MD5 Thumbprint:: - 832B3F18 707EA3F6 54465207 345A93F1 - Provider Type:: 1 Provider Name:: Microsoft Strong Cryptographic Provider Container: 68f79a6e-6afa-4ec7-be5b-16d6656edd3f KeySpec: 2 - NotBefore:: - Tue Jan 28 13:51:04 2025 - NotAfter:: - Sun Jan 01 05:29:59 2040 - ==============No CTLs ========== - ==============No CRLs ========== - ============================================== - CertMgr Succeeded".as_bytes().to_vec(), - stderr: vec![], - }; + test_build_action + .mock_package_task_runner + .expect_run() + .never(); - let expected_create_cert_output = Output { - status: ExitStatus::default(), - stdout: vec![], - stderr: vec![], - }; + let build_action = initialize_build_action(&mut test_build_action); + let result = build_action.run_from_workspace_root(&cwd); - let cargo_build_output = - create_cargo_build_output_json(driver_name, driver_version, &cwd, None, profile); - - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) - .set_up_standalone_driver_project((workspace_member, package)) - .expect_default_build_task_steps(driver_name, Some(cargo_build_output)) - .expect_probe_target_arch_using_cargo_rustc(&cwd, target_arch, None) - .expect_final_package_dir_exists(driver_name, &cwd, true) - .expect_inx_file_exists(driver_name, &cwd, true) - .expect_rename_driver_binary_dll_to_sys(driver_name, &cwd) - .expect_copy_driver_binary_sys_to_package_folder(driver_name, &cwd, true) - .expect_copy_pdb_file_to_package_folder(driver_name, &cwd, true) - .expect_copy_inx_file_to_package_folder(driver_name, &cwd, true, &cwd) - .expect_copy_map_file_to_package_folder(driver_name, &cwd, true) - .expect_stampinf(driver_name, &cwd, target_arch, None) - .expect_inf2cat(driver_name, &cwd, target_arch, None) - .expect_self_signed_cert_file_exists(&cwd, false) - .expect_certmgr_exists_check(Some(expected_certmgr_output)) - .expect_certmgr_create_cert_from_store(&cwd, Some(expected_create_cert_output)) - .expect_copy_self_signed_cert_file_to_package_folder(driver_name, &cwd, true) - .expect_signtool_sign_driver_binary_sys_file(driver_name, &cwd, None) - .expect_signtool_sign_cat_file(driver_name, &cwd, None) - .expect_infverif(driver_name, &cwd, "KMDF", None) - .expect_signtool_verify_driver_binary_sys_file(driver_name, &cwd, None) - .expect_signtool_verify_cat_file(driver_name, &cwd, None); - - assert_build_action_run_with_env_is_success( - &cwd, - profile, - None, - verify_signature, - sample_class, - test_build_action, + assert!( + result.is_ok(), + "build action failed unexpectedly: {result:?}" ); } #[test] -pub fn given_a_driver_project_when_final_package_dir_exists_then_it_should_skip_creating_it() { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; - let target_arch = CpuArchitecture::Amd64; - let verify_signature = false; - let sample_class = false; - - // Driver project data - let driver_type = "KMDF"; - let driver_name = "sample-kmdf"; - let driver_version = "0.0.1"; - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member, package) = - get_cargo_metadata_package(&cwd, driver_name, driver_version, Some(&wdk_metadata)); - let expected_certmgr_output = get_certmgr_success_output(); - - let cargo_build_output = - create_cargo_build_output_json(driver_name, driver_version, &cwd, None, profile); - - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) - .set_up_standalone_driver_project((workspace_member, package)) - .expect_default_build_task_steps(driver_name, Some(cargo_build_output)) - .expect_probe_target_arch_using_cargo_rustc(&cwd, target_arch, None) - .expect_final_package_dir_exists(driver_name, &cwd, false) - .expect_dir_created(driver_name, &cwd, true) - .expect_inx_file_exists(driver_name, &cwd, true) - .expect_rename_driver_binary_dll_to_sys(driver_name, &cwd) - .expect_copy_driver_binary_sys_to_package_folder(driver_name, &cwd, true) - .expect_copy_pdb_file_to_package_folder(driver_name, &cwd, true) - .expect_copy_inx_file_to_package_folder(driver_name, &cwd, true, &cwd) - .expect_copy_map_file_to_package_folder(driver_name, &cwd, true) - .expect_stampinf(driver_name, &cwd, target_arch, None) - .expect_inf2cat(driver_name, &cwd, target_arch, None) - .expect_self_signed_cert_file_exists(&cwd, false) - .expect_certmgr_exists_check(Some(expected_certmgr_output)) - .expect_makecert(&cwd, None) - .expect_copy_self_signed_cert_file_to_package_folder(driver_name, &cwd, true) - .expect_signtool_sign_driver_binary_sys_file(driver_name, &cwd, None) - .expect_signtool_sign_cat_file(driver_name, &cwd, None) - .expect_infverif(driver_name, &cwd, "KMDF", None); - - assert_build_action_run_with_env_is_success( - &cwd, - profile, +fn given_a_driver_package_without_cdylib_when_building_then_package_runner_is_skipped() { + let cwd = PathBuf::from(r"C:\tmp\driver-lib"); + let mut test_build_action = TestBuildAction::new( + cwd.clone(), None, - verify_signature, - sample_class, - test_build_action, - ); -} - -#[test] -pub fn given_a_driver_project_when_inx_file_do_not_exist_then_package_should_fail() { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; - let target_arch = CpuArchitecture::Amd64; - let verify_signature = true; - let sample_class = false; - - // Driver project data - let driver_type = "KMDF"; - let driver_name = "sample-kmdf"; - let driver_version = "0.0.1"; - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member, package) = - get_cargo_metadata_package(&cwd, driver_name, driver_version, Some(&wdk_metadata)); - - let cargo_build_output = - create_cargo_build_output_json(driver_name, driver_version, &cwd, None, profile); - - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) - .set_up_standalone_driver_project((workspace_member, package)) - .expect_default_build_task_steps(driver_name, Some(cargo_build_output)) - .expect_probe_target_arch_using_cargo_rustc(&cwd, target_arch, None) - .expect_inx_file_exists(driver_name, &cwd, false); - - let build_action = initialize_build_action( + None, + SignMode::Test { + verify_signature: false, + }, + false, + ) + .set_up_with_custom_toml(&get_cargo_metadata( &cwd, - profile.as_ref(), + vec![ + get_cargo_metadata_package_with_target( + &cwd, + DEFAULT_DRIVER_NAME, + DEFAULT_DRIVER_VERSION, + Some(default_wdk_metadata()), + "lib", + "lib", + "lib.rs", + ) + .1, + ], + &[get_cargo_metadata_package_with_target( + &cwd, + DEFAULT_DRIVER_NAME, + DEFAULT_DRIVER_VERSION, + Some(default_wdk_metadata()), + "lib", + "lib", + "lib.rs", + ) + .0], None, - verify_signature, - sample_class, - test_build_action, - ); - assert!(build_action.is_ok()); - let run_result = build_action.expect("Failed to init build action").run(); - - assert!(matches!( - run_result.as_ref().expect_err("expected error"), - BuildActionError::OneOrMoreWorkspaceMembersFailedToBuild(_) )); -} -#[test] -pub fn given_a_driver_project_when_copy_of_an_artifact_fails_then_the_package_should_fail() { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; - let target_arch = CpuArchitecture::Amd64; - let verify_signature = true; - let sample_class = false; - - // Driver project data - let driver_type = "KMDF"; - let driver_name = "sample-kmdf"; - let driver_version = "0.0.1"; - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member, package) = - get_cargo_metadata_package(&cwd, driver_name, driver_version, Some(&wdk_metadata)); - - let cargo_build_output = create_cargo_build_output_json( - driver_name, - driver_version, - &cwd, - Some(&to_target_triple(target_arch)), - profile, - ); + test_build_action.expect_build_runner(DEFAULT_DRIVER_NAME, &cwd, None, None, Ok(Vec::new())); + test_build_action + .mock_package_task_runner + .expect_run() + .never(); - let test_build_action = - &TestBuildAction::new(cwd.clone(), profile, Some(target_arch), sample_class) - .set_up_standalone_driver_project((workspace_member, package)) - .expect_default_build_task_steps(driver_name, Some(cargo_build_output)) - .expect_final_package_dir_exists(driver_name, &cwd, true) - .expect_inx_file_exists(driver_name, &cwd, true) - .expect_rename_driver_binary_dll_to_sys(driver_name, &cwd) - .expect_copy_driver_binary_sys_to_package_folder(driver_name, &cwd, false); + let build_action = initialize_build_action(&mut test_build_action); + let result = build_action.run_from_workspace_root(&cwd); - let build_action = initialize_build_action( - &cwd, - profile.as_ref(), - Some(target_arch), - verify_signature, - sample_class, - test_build_action, + assert!( + result.is_ok(), + "build action failed unexpectedly: {result:?}" ); - assert!(build_action.is_ok()); - let run_result = build_action.expect("Failed to init build action").run(); - - assert!(matches!( - run_result.as_ref().expect_err("expected error"), - BuildActionError::OneOrMoreWorkspaceMembersFailedToBuild(_) - )); } #[test] -pub fn given_a_driver_project_when_stampinf_command_execution_fails_then_package_should_fail() { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; +fn given_a_workspace_root_when_one_member_fails_then_workspace_error_is_returned() { + let workspace_root = PathBuf::from(r"C:\tmp\workspace"); + let driver_name_1 = "sample-kmdf-1"; + let driver_name_2 = "sample-kmdf-2"; + let driver_dir_1 = workspace_root.join(driver_name_1); + let driver_dir_2 = workspace_root.join(driver_name_2); let target_arch = CpuArchitecture::Amd64; - let verify_signature = true; - let sample_class = false; - - // Driver project data - let driver_type = "KMDF"; - let driver_name = "sample-kmdf"; - let driver_version = "0.0.1"; - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member, package) = - get_cargo_metadata_package(&cwd, driver_name, driver_version, Some(&wdk_metadata)); - - let expected_stampinf_output = Output { - status: ExitStatus::from_raw(1), - stdout: vec![], - stderr: vec![], - }; - - let cargo_build_output = - create_cargo_build_output_json(driver_name, driver_version, &cwd, None, profile); - - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) - .set_up_standalone_driver_project((workspace_member, package)) - .expect_default_build_task_steps(driver_name, Some(cargo_build_output)) - .expect_probe_target_arch_using_cargo_rustc(&cwd, target_arch, None) - .expect_final_package_dir_exists(driver_name, &cwd, true) - .expect_inx_file_exists(driver_name, &cwd, true) - .expect_rename_driver_binary_dll_to_sys(driver_name, &cwd) - .expect_copy_driver_binary_sys_to_package_folder(driver_name, &cwd, true) - .expect_copy_pdb_file_to_package_folder(driver_name, &cwd, true) - .expect_copy_inx_file_to_package_folder(driver_name, &cwd, true, &cwd) - .expect_copy_map_file_to_package_folder(driver_name, &cwd, true) - .expect_stampinf( - driver_name, - &cwd, - target_arch, - Some(expected_stampinf_output), - ); - - let build_action = initialize_build_action( - &cwd, - profile.as_ref(), + let target_dir_1 = expected_target_dir(&driver_dir_1, Some(target_arch), None); + let target_triple = to_target_triple(target_arch); + let mut test_build_action = TestBuildAction::new( + workspace_root.clone(), None, - verify_signature, - sample_class, - test_build_action, + Some(target_arch), + SignMode::Test { + verify_signature: false, + }, + false, + ) + .set_up_workspace_with_multiple_driver_projects( + &workspace_root, + vec![ + ( + driver_name_1, + driver_dir_1.clone(), + Some(default_wdk_metadata()), + "cdylib", + "cdylib", + "main.rs", + ), + ( + driver_name_2, + driver_dir_2.clone(), + Some(default_wdk_metadata()), + "cdylib", + "cdylib", + "main.rs", + ), + ], ); - assert!(build_action.is_ok()); - let run_result = run_build_action(build_action); - - assert!(matches!( - run_result.as_ref().expect_err("expected error"), - BuildActionError::OneOrMoreWorkspaceMembersFailedToBuild(_) - )); -} - -#[test] -pub fn given_a_driver_project_when_inf2cat_command_execution_fails_then_package_should_fail() { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; - let target_arch = CpuArchitecture::Amd64; - let verify_signature = true; - let sample_class = false; - - // Driver project data - let driver_type = "KMDF"; - let driver_name = "sample-kmdf"; - let driver_version = "0.0.1"; - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member, package) = - get_cargo_metadata_package(&cwd, driver_name, driver_version, Some(&wdk_metadata)); - - let expected_inf2cat_output = Output { - status: ExitStatus::from_raw(1), - stdout: vec![], - stderr: vec![], - }; - - let cargo_build_output = - create_cargo_build_output_json(driver_name, driver_version, &cwd, None, profile); - - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) - .set_up_standalone_driver_project((workspace_member, package)) - .expect_default_build_task_steps(driver_name, Some(cargo_build_output)) - .expect_probe_target_arch_using_cargo_rustc(&cwd, target_arch, None) - .expect_final_package_dir_exists(driver_name, &cwd, true) - .expect_inx_file_exists(driver_name, &cwd, true) - .expect_rename_driver_binary_dll_to_sys(driver_name, &cwd) - .expect_copy_driver_binary_sys_to_package_folder(driver_name, &cwd, true) - .expect_copy_pdb_file_to_package_folder(driver_name, &cwd, true) - .expect_copy_inx_file_to_package_folder(driver_name, &cwd, true, &cwd) - .expect_copy_map_file_to_package_folder(driver_name, &cwd, true) - .expect_stampinf(driver_name, &cwd, target_arch, None) - .expect_inf2cat( - driver_name, - &cwd, - target_arch, - Some(expected_inf2cat_output), - ); - let build_action = initialize_build_action( - &cwd, - profile.as_ref(), + test_build_action.expect_build_runner( + driver_name_1, + &driver_dir_1, + None, + Some(target_arch), + Ok(cargo_build_messages( + driver_name_1, + DEFAULT_DRIVER_VERSION, + &driver_dir_1, + Some(target_triple.as_str()), + None, + )), + ); + test_build_action.expect_build_runner( + driver_name_2, + &driver_dir_2, None, - verify_signature, - sample_class, - test_build_action, + Some(target_arch), + Err(build_task_error()), + ); + test_build_action.expect_package_runner( + driver_name_1, + &driver_dir_1, + &target_dir_1, + target_arch, + SignMode::Test { + verify_signature: false, + }, + false, ); - assert!(build_action.is_ok()); - let run_result = run_build_action(build_action); + + let build_action = initialize_build_action(&mut test_build_action); + let result = build_action.run_from_workspace_root(&workspace_root); assert!(matches!( - run_result.as_ref().expect_err("expected error"), - BuildActionError::OneOrMoreWorkspaceMembersFailedToBuild(_) + result, + Err(BuildActionError::OneOrMoreWorkspaceMembersFailedToBuild(path)) + if path == workspace_root )); } #[test] -pub fn given_a_driver_project_when_certmgr_command_execution_fails_then_package_should_fail() { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; +fn given_a_workspace_member_directory_when_building_then_only_that_member_is_orchestrated() { + let workspace_root = PathBuf::from(r"C:\tmp\workspace"); + let driver_name_1 = "sample-kmdf-1"; + let driver_name_2 = "sample-kmdf-2"; + let driver_dir_1 = workspace_root.join(driver_name_1); + let driver_dir_2 = workspace_root.join(driver_name_2); let target_arch = CpuArchitecture::Amd64; - let verify_signature = true; - let sample_class = false; - - // Driver project data - let driver_type = "KMDF"; - let driver_name = "sample-kmdf"; - let driver_version = "0.0.1"; - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member, package) = - get_cargo_metadata_package(&cwd, driver_name, driver_version, Some(&wdk_metadata)); - - let expected_output = Output { - status: ExitStatus::from_raw(1), - stdout: vec![], - stderr: vec![], - }; + let target_dir_2 = expected_target_dir(&driver_dir_2, Some(target_arch), None); + let target_triple = to_target_triple(target_arch); + let mut test_build_action = TestBuildAction::new( + driver_dir_2.clone(), + None, + Some(target_arch), + SignMode::Test { + verify_signature: false, + }, + false, + ) + .set_up_workspace_with_multiple_driver_projects( + &workspace_root, + vec![ + ( + driver_name_1, + driver_dir_1, + Some(default_wdk_metadata()), + "cdylib", + "cdylib", + "main.rs", + ), + ( + driver_name_2, + driver_dir_2.clone(), + Some(default_wdk_metadata()), + "cdylib", + "cdylib", + "main.rs", + ), + ], + ); - let cargo_build_output = - create_cargo_build_output_json(driver_name, driver_version, &cwd, None, profile); - - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) - .set_up_standalone_driver_project((workspace_member, package)) - .expect_default_build_task_steps(driver_name, Some(cargo_build_output)) - .expect_probe_target_arch_using_cargo_rustc(&cwd, target_arch, None) - .expect_final_package_dir_exists(driver_name, &cwd, true) - .expect_inx_file_exists(driver_name, &cwd, true) - .expect_rename_driver_binary_dll_to_sys(driver_name, &cwd) - .expect_copy_driver_binary_sys_to_package_folder(driver_name, &cwd, true) - .expect_copy_pdb_file_to_package_folder(driver_name, &cwd, true) - .expect_copy_inx_file_to_package_folder(driver_name, &cwd, true, &cwd) - .expect_copy_map_file_to_package_folder(driver_name, &cwd, true) - .expect_stampinf(driver_name, &cwd, target_arch, None) - .expect_inf2cat(driver_name, &cwd, target_arch, None) - .expect_infverif(driver_name, &cwd, driver_type, None) - .expect_self_signed_cert_file_exists(&cwd, false) - .expect_certmgr_exists_check(Some(expected_output)); - - let build_action = initialize_build_action( - &cwd, - profile.as_ref(), + test_build_action.expect_build_runner( + driver_name_2, + &driver_dir_2, None, - verify_signature, - sample_class, - test_build_action, + Some(target_arch), + Ok(cargo_build_messages( + driver_name_2, + DEFAULT_DRIVER_VERSION, + &driver_dir_2, + Some(target_triple.as_str()), + None, + )), + ); + test_build_action.expect_package_runner( + driver_name_2, + &driver_dir_2, + &target_dir_2, + target_arch, + SignMode::Test { + verify_signature: false, + }, + false, ); - assert!(build_action.is_ok()); - let run_result = run_build_action(build_action); - assert!(matches!( - run_result.as_ref().expect_err("expected error"), - BuildActionError::OneOrMoreWorkspaceMembersFailedToBuild(_) - )); + let build_action = initialize_build_action(&mut test_build_action); + let result = build_action.run_from_workspace_root(&driver_dir_2); + + assert!( + result.is_ok(), + "build action failed unexpectedly: {result:?}" + ); } #[test] -pub fn given_a_driver_project_when_makecert_command_execution_fails_then_package_should_fail() { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; +fn given_an_emulated_workspace_when_running_then_each_valid_project_is_built() { + let emulated_workspace = TestWorkspaceRoot::new("build-action-emulated-workspace"); + let driver_name_1 = "driver-a"; + let driver_name_2 = "driver-b"; + let ignored_dir = "docs"; + let driver_dir_1 = emulated_workspace.root.join(driver_name_1); + let driver_dir_2 = emulated_workspace.root.join(driver_name_2); + let ignored_dir_path = emulated_workspace.root.join(ignored_dir); + fs::create_dir_all(&driver_dir_1).expect("failed to create driver-a directory"); + fs::create_dir_all(&driver_dir_2).expect("failed to create driver-b directory"); + fs::create_dir_all(&ignored_dir_path).expect("failed to create docs directory"); + let target_arch = CpuArchitecture::Amd64; - let verify_signature = true; - let sample_class = false; - - // Driver project data - let driver_type = "KMDF"; - let driver_name = "sample-kmdf"; - let driver_version = "0.0.1"; - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member, package) = - get_cargo_metadata_package(&cwd, driver_name, driver_version, Some(&wdk_metadata)); - - let expected_output = Output { - status: ExitStatus::from_raw(1), - stdout: vec![], - stderr: vec![], - }; + let target_dir_1 = expected_target_dir(&driver_dir_1, Some(target_arch), None); + let target_dir_2 = expected_target_dir(&driver_dir_2, Some(target_arch), None); + let target_triple = to_target_triple(target_arch); + let mut test_build_action = TestBuildAction::new( + emulated_workspace.root.clone(), + None, + Some(target_arch), + SignMode::Test { + verify_signature: false, + }, + false, + ); - let cargo_build_output = - create_cargo_build_output_json(driver_name, driver_version, &cwd, None, profile); - - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) - .set_up_standalone_driver_project((workspace_member, package)) - .expect_default_build_task_steps(driver_name, Some(cargo_build_output)) - .expect_probe_target_arch_using_cargo_rustc(&cwd, target_arch, None) - .expect_final_package_dir_exists(driver_name, &cwd, true) - .expect_inx_file_exists(driver_name, &cwd, true) - .expect_rename_driver_binary_dll_to_sys(driver_name, &cwd) - .expect_copy_driver_binary_sys_to_package_folder(driver_name, &cwd, true) - .expect_copy_pdb_file_to_package_folder(driver_name, &cwd, true) - .expect_copy_inx_file_to_package_folder(driver_name, &cwd, true, &cwd) - .expect_copy_map_file_to_package_folder(driver_name, &cwd, true) - .expect_stampinf(driver_name, &cwd, target_arch, None) - .expect_inf2cat(driver_name, &cwd, target_arch, None) - .expect_infverif(driver_name, &cwd, driver_type, None) - .expect_self_signed_cert_file_exists(&cwd, false) - .expect_certmgr_exists_check(None) - .expect_makecert(&cwd, Some(expected_output)); - - let build_action = initialize_build_action( - &cwd, - profile.as_ref(), + test_build_action.expect_detect_wdk_build_number(25100); + test_build_action.expect_root_manifest_exists(&emulated_workspace.root, false); + test_build_action.expect_read_dir_entries(&emulated_workspace.root); + test_build_action.expect_dir_cargo_toml_exists(&driver_dir_1, true); + test_build_action.expect_dir_cargo_toml_exists(&driver_dir_1, true); + test_build_action.expect_dir_cargo_toml_exists(&driver_dir_2, true); + test_build_action.expect_dir_cargo_toml_exists(&ignored_dir_path, false); + test_build_action.expect_dir_cargo_toml_exists(&ignored_dir_path, false); + test_build_action.expect_metadata_for_paths(vec![ + ( + driver_dir_1.clone(), + metadata_from_packages( + &driver_dir_1, + vec![( + driver_name_1, + driver_dir_1.clone(), + Some(default_wdk_metadata()), + "cdylib", + "cdylib", + "main.rs", + )], + ), + ), + ( + driver_dir_2.clone(), + metadata_from_packages( + &driver_dir_2, + vec![( + driver_name_2, + driver_dir_2.clone(), + Some(default_wdk_metadata()), + "cdylib", + "cdylib", + "main.rs", + )], + ), + ), + ]); + test_build_action.expect_build_runner( + driver_name_1, + &driver_dir_1, + None, + Some(target_arch), + Ok(cargo_build_messages( + driver_name_1, + DEFAULT_DRIVER_VERSION, + &driver_dir_1, + Some(target_triple.as_str()), + None, + )), + ); + test_build_action.expect_build_runner( + driver_name_2, + &driver_dir_2, None, - verify_signature, - sample_class, - test_build_action, + Some(target_arch), + Ok(cargo_build_messages( + driver_name_2, + DEFAULT_DRIVER_VERSION, + &driver_dir_2, + Some(target_triple.as_str()), + None, + )), + ); + test_build_action.expect_package_runner( + driver_name_1, + &driver_dir_1, + &target_dir_1, + target_arch, + SignMode::Test { + verify_signature: false, + }, + false, + ); + test_build_action.expect_package_runner( + driver_name_2, + &driver_dir_2, + &target_dir_2, + target_arch, + SignMode::Test { + verify_signature: false, + }, + false, ); - assert!(build_action.is_ok()); - let run_result = run_build_action(build_action); - assert!(matches!( - run_result.as_ref().expect_err("expected error"), - BuildActionError::OneOrMoreWorkspaceMembersFailedToBuild(_) - )); + let build_action = initialize_build_action(&mut test_build_action); + let result = crate::test_utils::with_env::<&str, &str, _, _>(&[], || build_action.run()); + + assert!( + result.is_ok(), + "build action failed unexpectedly: {result:?}" + ); } #[test] -pub fn given_a_driver_project_when_signtool_command_execution_fails_then_package_should_fail() { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; - let target_arch = CpuArchitecture::Amd64; - let verify_signature = true; - let sample_class = false; - - // Driver project data - let driver_type = "KMDF"; - let driver_name = "sample-kmdf"; - let driver_version = "0.0.1"; - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member, package) = - get_cargo_metadata_package(&cwd, driver_name, driver_version, Some(&wdk_metadata)); - - let cargo_build_output = - create_cargo_build_output_json(driver_name, driver_version, &cwd, None, profile); - - let expected_output = Output { - status: ExitStatus::from_raw(1), - stdout: vec![], - stderr: vec![], - }; +fn given_a_workspace_member_when_build_runner_fails_then_the_build_error_is_propagated() { + let workspace_root = PathBuf::from(r"C:\tmp\workspace"); + let cwd = workspace_root.join(DEFAULT_DRIVER_NAME); + let mut test_build_action = TestBuildAction::new( + cwd.clone(), + None, + None, + SignMode::Test { + verify_signature: false, + }, + false, + ) + .set_up_workspace_with_multiple_driver_projects( + &workspace_root, + vec![( + DEFAULT_DRIVER_NAME, + cwd.clone(), + Some(default_wdk_metadata()), + "cdylib", + "cdylib", + "main.rs", + )], + ); - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) - .set_up_standalone_driver_project((workspace_member, package)) - .expect_default_build_task_steps(driver_name, Some(cargo_build_output)) - .expect_probe_target_arch_using_cargo_rustc(&cwd, target_arch, None) - .expect_final_package_dir_exists(driver_name, &cwd, true) - .expect_inx_file_exists(driver_name, &cwd, true) - .expect_rename_driver_binary_dll_to_sys(driver_name, &cwd) - .expect_copy_driver_binary_sys_to_package_folder(driver_name, &cwd, true) - .expect_copy_pdb_file_to_package_folder(driver_name, &cwd, true) - .expect_copy_inx_file_to_package_folder(driver_name, &cwd, true, &cwd) - .expect_copy_map_file_to_package_folder(driver_name, &cwd, true) - .expect_stampinf(driver_name, &cwd, target_arch, None) - .expect_inf2cat(driver_name, &cwd, target_arch, None) - .expect_infverif(driver_name, &cwd, driver_type, None) - .expect_self_signed_cert_file_exists(&cwd, false) - .expect_certmgr_exists_check(None) - .expect_makecert(&cwd, None) - .expect_copy_self_signed_cert_file_to_package_folder(driver_name, &cwd, true) - .expect_signtool_sign_driver_binary_sys_file(driver_name, &cwd, Some(expected_output)); - - let build_action = initialize_build_action( + test_build_action.expect_build_runner( + DEFAULT_DRIVER_NAME, &cwd, - profile.as_ref(), None, - verify_signature, - sample_class, - test_build_action, + None, + Err(build_task_error()), ); - assert!(build_action.is_ok()); - let run_result = run_build_action(build_action); - assert!(matches!( - run_result.as_ref().expect_err("expected error"), - BuildActionError::OneOrMoreWorkspaceMembersFailedToBuild(_) - )); -} + let build_action = initialize_build_action(&mut test_build_action); + let result = build_action.run_from_workspace_root(&cwd); -#[test] -pub fn given_a_driver_project_when_infverif_command_execution_fails_then_package_should_fail() { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; - let target_arch = CpuArchitecture::Amd64; - let verify_signature = true; - let sample_class = false; - - // Driver project data - let driver_type = "KMDF"; - let driver_name = "sample-kmdf"; - let driver_version = "0.0.1"; - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member, package) = - get_cargo_metadata_package(&cwd, driver_name, driver_version, Some(&wdk_metadata)); - - let cargo_build_output = - create_cargo_build_output_json(driver_name, driver_version, &cwd, None, profile); - - let expected_output = Output { - status: ExitStatus::from_raw(1), - stdout: vec![], - stderr: vec![], - }; + assert!(matches!(result, Err(BuildActionError::BuildTask(_)))); +} - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) - .set_up_standalone_driver_project((workspace_member, package)) - .expect_default_build_task_steps(driver_name, Some(cargo_build_output)) - .expect_probe_target_arch_using_cargo_rustc(&cwd, target_arch, None) - .expect_final_package_dir_exists(driver_name, &cwd, true) - .expect_inx_file_exists(driver_name, &cwd, true) - .expect_rename_driver_binary_dll_to_sys(driver_name, &cwd) - .expect_copy_driver_binary_sys_to_package_folder(driver_name, &cwd, true) - .expect_copy_pdb_file_to_package_folder(driver_name, &cwd, true) - .expect_copy_inx_file_to_package_folder(driver_name, &cwd, true, &cwd) - .expect_copy_map_file_to_package_folder(driver_name, &cwd, true) - .expect_stampinf(driver_name, &cwd, target_arch, None) - .expect_inf2cat(driver_name, &cwd, target_arch, None) - .expect_infverif(driver_name, &cwd, driver_type, Some(expected_output)); - - let build_action = initialize_build_action( - &cwd, - profile.as_ref(), - None, - verify_signature, - sample_class, - test_build_action, - ); - assert!(build_action.is_ok()); - let run_result = run_build_action(build_action); - - assert!(matches!( - run_result.as_ref().expect_err("expected error"), - BuildActionError::OneOrMoreWorkspaceMembersFailedToBuild(_) - )); -} - -#[test] -pub fn given_a_non_driver_project_when_default_values_are_provided_with_no_wdk_metadata_are_provided_then_build_should_be_successful() - { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; - let verify_signature = true; - let sample_class = false; - - // Driver project data - let driver_name = "non-driver"; - let driver_version = "0.0.1"; - let (workspace_member, package) = - get_cargo_metadata_package(&cwd, driver_name, driver_version, None); - - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) - .set_up_standalone_driver_project((workspace_member, package)) - .expect_default_build_task_steps(driver_name, None); - - assert_build_action_run_is_success( - &cwd, - profile, - None, - verify_signature, - sample_class, - test_build_action, - ); -} - -#[test] -pub fn given_a_invalid_driver_project_with_partial_wdk_metadata_when_valid_default_values_are_provided_then_wdk_metadata_parse_should_fail() - { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp\\sample-driver"); - let profile = None; - let verify_signature = true; - let sample_class = false; - - // Driver project data - let driver_name = "sample-driver"; - let cargo_toml_metadata = invalid_driver_cargo_toml(); - - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) - .set_up_with_custom_toml(&cargo_toml_metadata) - .expect_default_build_task_steps(driver_name, None); - - let build_action = initialize_build_action( - &cwd, - profile.as_ref(), - None, - verify_signature, - sample_class, - test_build_action, - ); - assert!(build_action.is_ok()); - let run_result = build_action.expect("Failed to init build action").run(); - assert!(matches!( - run_result.as_ref().expect_err("expected error"), - BuildActionError::WdkMetadataParse(TryFromCargoMetadataError::WdkMetadataDeserialization { - metadata_source: _, - error_source: _ - }) - )); -} - -#[test] -pub fn given_a_driver_project_when_target_arch_is_not_provided_and_probing_cargo_rustc_fails_then_packaging_should_fail() - { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; - let verify_signature = false; - let sample_class = false; - // Driver project data - let driver_type = "KMDF"; - let driver_name = "sample-kmdf"; - let driver_version = "0.0.1"; - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member, package) = - get_cargo_metadata_package(&cwd, driver_name, driver_version, Some(&wdk_metadata)); - - let cargo_build_output = - create_cargo_build_output_json(driver_name, driver_version, &cwd, None, profile); - - let cargo_rustc_output = Output { - status: ExitStatus::from_raw(1), - stdout: vec![], - stderr: vec![], - }; - - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) - .set_up_standalone_driver_project((workspace_member, package)) - .expect_default_build_task_steps(driver_name, Some(cargo_build_output)) - .expect_probe_target_arch_using_cargo_rustc( - &cwd, - CpuArchitecture::Amd64, - Some(cargo_rustc_output), - ); - - let build_action = initialize_build_action( - &cwd, - profile.as_ref(), - None, - verify_signature, - sample_class, - test_build_action, - ); - assert!(build_action.is_ok()); - let run_result = build_action.expect("Failed to init build action").run(); - assert!(matches!( - run_result.as_ref().expect_err("expected error"), - BuildActionError::OneOrMoreWorkspaceMembersFailedToBuild(_) - )); -} - -//////////////////////////////////////////////////////////////////////////////// -/// Workspace tests -//////////////////////////////////////////////////////////////////////////////// -#[test] -pub fn given_a_workspace_with_multiple_driver_and_non_driver_projects_when_default_values_are_provided_then_it_packages_successfully() - { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; - let target_arch = CpuArchitecture::Amd64; - let verify_signature = true; - let sample_class = false; - - // Driver project data - let driver_type = "KMDF"; - let driver_name_1 = "sample-kmdf-1"; - let driver_version_1 = "0.0.1"; - let driver_name_2 = "sample-kmdf-2"; - let driver_version_2 = "0.0.2"; - let non_driver = "non-driver"; - let non_driver_version = "0.0.3"; - - // Create artifact outputs for workspace packages - let artifact_1 = create_cargo_build_output_json_with_manifest( - driver_name_1, - driver_version_1, - &cwd, - &cwd.join(driver_name_1).join("Cargo.toml"), - None, - profile, - true, - ); - let artifact_2 = create_cargo_build_output_json_with_manifest( - driver_name_2, - driver_version_2, - &cwd, - &cwd.join(driver_name_2).join("Cargo.toml"), - None, - profile, - true, - ); - let artifact_non_driver = create_cargo_build_output_json_with_manifest( - non_driver, - non_driver_version, - &cwd, - &cwd.join(non_driver).join("Cargo.toml"), - None, - profile, - false, - ); - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member_1, package_1) = get_cargo_metadata_package( - &cwd.join(driver_name_1), - driver_name_1, - driver_version_1, - Some(&wdk_metadata), - ); - let (workspace_member_2, package_2) = get_cargo_metadata_package( - &cwd.join(driver_name_2), - driver_name_2, - driver_version_2, - Some(&wdk_metadata), - ); - let (workspace_member_3, package_3) = - get_cargo_metadata_package(&cwd.join(non_driver), non_driver, non_driver_version, None); - - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) - .set_up_workspace_with_multiple_driver_projects( - &cwd, - Some(wdk_metadata), - vec![ - (workspace_member_1, package_1), - (workspace_member_2, package_2), - (workspace_member_3, package_3), - ], - ) - .expect_detect_wdk_build_number(25100u32) - .expect_root_manifest_exists(&cwd, true) - .expect_cargo_build(driver_name_1, &cwd.join(driver_name_1), Some(artifact_1)) - .expect_probe_target_arch_using_cargo_rustc(&cwd.join(driver_name_1), target_arch, None) - .expect_default_package_task_steps_for_workspace( - driver_name_1, - driver_type, - target_arch, - verify_signature, - ) - // Second driver project - .expect_cargo_build(driver_name_2, &cwd.join(driver_name_2), Some(artifact_2)) - .expect_probe_target_arch_using_cargo_rustc(&cwd.join(driver_name_2), target_arch, None) - .expect_default_package_task_steps_for_workspace( - driver_name_2, - driver_type, - target_arch, - verify_signature, - ) - // Non-driver project - .expect_cargo_build(non_driver, &cwd.join(non_driver), Some(artifact_non_driver)); - - assert_build_action_run_with_env_is_success( - &cwd, - profile, - None, - verify_signature, - sample_class, - test_build_action, - ); -} - -#[test] -pub fn given_a_workspace_with_multiple_driver_and_non_driver_projects_when_cwd_is_driver_project_then_it_packages_driver_project_successfully() - { - // Input CLI args - let workspace_root_dir = PathBuf::from("C:\\tmp"); - let cwd = workspace_root_dir.join("sample-kmdf-1"); - let profile = None; - let target_arch = CpuArchitecture::Amd64; - let verify_signature = true; - let sample_class = false; - - // Driver project data - let driver_type = "KMDF"; - let driver_name_1 = "sample-kmdf-1"; - let driver_version_1 = "0.0.1"; - let driver_name_2 = "sample-kmdf-2"; - let driver_version_2 = "0.0.2"; - let non_driver = "non-driver"; - let non_driver_version = "0.0.3"; - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member_1, package_1) = get_cargo_metadata_package( - &workspace_root_dir.join(driver_name_1), - driver_name_1, - driver_version_1, - Some(&wdk_metadata), - ); - let (workspace_member_2, package_2) = get_cargo_metadata_package( - &workspace_root_dir.join(driver_name_2), - driver_name_2, - driver_version_2, - Some(&wdk_metadata), - ); - let (workspace_member_3, package_3) = get_cargo_metadata_package( - &workspace_root_dir.join(non_driver), - non_driver, - non_driver_version, - None, - ); - - let expected_certmgr_output = get_certmgr_success_output(); - - let cargo_build_output = create_cargo_build_output_json_with_manifest( - driver_name_1, - driver_version_1, - &workspace_root_dir, - &workspace_root_dir.join(driver_name_1).join("Cargo.toml"), - None, - profile, - true, - ); - - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) // Even when cwd is changed to driver project inside the workspace, cargo metadata read - // is going to be for the whole workspace - .set_up_workspace_with_multiple_driver_projects( - &workspace_root_dir, - Some(wdk_metadata), - vec![ - (workspace_member_1, package_1), - (workspace_member_2, package_2), - (workspace_member_3, package_3), - ], - ) - .expect_detect_wdk_build_number(25100u32) - .expect_root_manifest_exists(&cwd, true) - .expect_cargo_build(driver_name_1, &cwd, Some(cargo_build_output)) - .expect_probe_target_arch_using_cargo_rustc(&cwd, target_arch, None) - .expect_final_package_dir_exists(driver_name_1, &workspace_root_dir, true) - .expect_inx_file_exists(driver_name_1, &cwd, true) - .expect_rename_driver_binary_dll_to_sys(driver_name_1, &workspace_root_dir) - .expect_copy_driver_binary_sys_to_package_folder(driver_name_1, &workspace_root_dir, true) - .expect_copy_pdb_file_to_package_folder(driver_name_1, &workspace_root_dir, true) - .expect_copy_inx_file_to_package_folder(driver_name_1, &cwd, true, &workspace_root_dir) - .expect_copy_map_file_to_package_folder(driver_name_1, &workspace_root_dir, true) - .expect_stampinf(driver_name_1, &workspace_root_dir, target_arch, None) - .expect_inf2cat(driver_name_1, &workspace_root_dir, target_arch, None) - .expect_self_signed_cert_file_exists(&workspace_root_dir, false) - .expect_certmgr_exists_check(Some(expected_certmgr_output)) - .expect_makecert(&workspace_root_dir, None) - .expect_copy_self_signed_cert_file_to_package_folder( - driver_name_1, - &workspace_root_dir, - true, - ) - .expect_signtool_sign_driver_binary_sys_file(driver_name_1, &workspace_root_dir, None) - .expect_signtool_sign_cat_file(driver_name_1, &workspace_root_dir, None) - .expect_signtool_verify_driver_binary_sys_file(driver_name_1, &workspace_root_dir, None) - .expect_signtool_verify_cat_file(driver_name_1, &workspace_root_dir, None) - .expect_infverif(driver_name_1, &workspace_root_dir, "KMDF", None); - - assert_build_action_run_with_env_is_success( - &cwd, - profile, - None, - verify_signature, - sample_class, - test_build_action, - ); -} - -#[test] -pub fn given_a_workspace_with_multiple_driver_and_non_driver_projects_when_verify_signature_is_false_then_it_skips_verify_tasks() - { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; - let target_arch = CpuArchitecture::Amd64; - let verify_signature = false; - let sample_class = false; - - // Driver project data - let driver_type = "KMDF"; - let driver_name_1 = "sample-kmdf-1"; - let driver_version_1 = "0.0.1"; - let driver_name_2 = "sample-kmdf-2"; - let driver_version_2 = "0.0.2"; - let non_driver = "non-driver"; - let non_driver_version = "0.0.3"; - - // Create artifact outputs for workspace packages - let artifact_1 = create_cargo_build_output_json_with_manifest( - driver_name_1, - driver_version_1, - &cwd, - &cwd.join(driver_name_1).join("Cargo.toml"), - None, - profile, - true, - ); - let artifact_2 = create_cargo_build_output_json_with_manifest( - driver_name_2, - driver_version_2, - &cwd, - &cwd.join(driver_name_2).join("Cargo.toml"), - None, - profile, - true, - ); - let artifact_non_driver = create_cargo_build_output_json_with_manifest( - non_driver, - non_driver_version, - &cwd, - &cwd.join(non_driver).join("Cargo.toml"), - None, - profile, - false, // NOT a driver - will use "lib" instead of "cdylib" - ); - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member_1, package_1) = get_cargo_metadata_package( - &cwd.join(driver_name_1), - driver_name_1, - driver_version_1, - Some(&wdk_metadata), - ); - let (workspace_member_2, package_2) = get_cargo_metadata_package( - &cwd.join(driver_name_2), - driver_name_2, - driver_version_2, - Some(&wdk_metadata), - ); - let (workspace_member_3, package_3) = - get_cargo_metadata_package(&cwd.join(non_driver), non_driver, non_driver_version, None); - - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) - .set_up_workspace_with_multiple_driver_projects( - &cwd, - Some(wdk_metadata), - vec![ - (workspace_member_1, package_1), - (workspace_member_2, package_2), - (workspace_member_3, package_3), - ], - ) - .expect_detect_wdk_build_number(25100u32) - .expect_root_manifest_exists(&cwd, true) - .expect_cargo_build(driver_name_1, &cwd.join(driver_name_1), Some(artifact_1)) - .expect_probe_target_arch_using_cargo_rustc(&cwd.join(driver_name_1), target_arch, None) - .expect_default_package_task_steps_for_workspace( - driver_name_1, - driver_type, - target_arch, - verify_signature, - ) - // Second driver project - .expect_cargo_build(driver_name_2, &cwd.join(driver_name_2), Some(artifact_2)) - .expect_probe_target_arch_using_cargo_rustc(&cwd.join(driver_name_2), target_arch, None) - .expect_default_package_task_steps_for_workspace( - driver_name_2, - driver_type, - target_arch, - verify_signature, - ) - // Non-driver project - .expect_cargo_build(non_driver, &cwd.join(non_driver), Some(artifact_non_driver)); - - assert_build_action_run_with_env_is_success( - &cwd, - profile, - None, - verify_signature, - sample_class, - test_build_action, - ); -} - -#[test] -pub fn given_a_workspace_with_multiple_driver_and_non_driver_projects_when_cwd_is_non_driver_project_then_it_builds_but_skips_packaging() - { - // Input CLI args - let workspace_root_dir = PathBuf::from("C:\\tmp"); - let cwd = workspace_root_dir.join("non-driver"); - let profile = None; - let verify_signature = true; - let sample_class = false; - - // Driver project data - let driver_type = "KMDF"; - let driver_name_1 = "sample-kmdf-1"; - let driver_version_1 = "0.0.1"; - let driver_name_2 = "sample-kmdf-2"; - let driver_version_2 = "0.0.2"; - let non_driver = "non-driver"; - let non_driver_version = "0.0.3"; - let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33); - let (workspace_member_1, package_1) = get_cargo_metadata_package( - &workspace_root_dir.join(driver_name_1), - driver_name_1, - driver_version_1, - Some(&wdk_metadata), - ); - let (workspace_member_2, package_2) = get_cargo_metadata_package( - &workspace_root_dir.join(driver_name_2), - driver_name_2, - driver_version_2, - Some(&wdk_metadata), - ); - let (workspace_member_3, package_3) = get_cargo_metadata_package( - &workspace_root_dir.join(non_driver), - non_driver, - non_driver_version, - None, - ); - - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) // Even when cwd is changed to driver project inside the workspace, cargo metadata read - // is going to be for the whole workspace - .set_up_workspace_with_multiple_driver_projects( - &workspace_root_dir, - Some(wdk_metadata), - vec![ - (workspace_member_1, package_1), - (workspace_member_2, package_2), - (workspace_member_3, package_3), - ], - ) - .expect_detect_wdk_build_number(25100u32) - .expect_root_manifest_exists(&cwd, true) - .expect_cargo_build(non_driver, &cwd, None); - - assert_build_action_run_is_success( - &cwd, - profile, - None, - verify_signature, - sample_class, - test_build_action, - ); -} - -#[test] -pub fn given_a_workspace_with_multiple_distinct_wdk_configurations_at_each_workspace_member_level_when_default_values_are_provided_then_wdk_metadata_parse_should_fail() - { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; - let verify_signature = true; - let sample_class = false; - - // Driver project data - let driver_type_1 = "KMDF"; - let driver_name_1 = "sample-kmdf-1"; - let driver_type_2 = "UMDF"; - let driver_version_1 = "0.0.1"; - let driver_name_2 = "sample-kmdf-2"; - let driver_version_2 = "0.0.2"; - let wdk_metadata_1 = get_cargo_metadata_wdk_metadata(driver_type_1, 1, 33); - let wdk_metadata_2 = get_cargo_metadata_wdk_metadata(driver_type_2, 1, 33); - let (workspace_member_1, package_1) = get_cargo_metadata_package( - &cwd.join(driver_name_1), - driver_name_1, - driver_version_1, - Some(&wdk_metadata_1), - ); - let (workspace_member_2, package_2) = get_cargo_metadata_package( - &cwd.join(driver_name_2), - driver_name_2, - driver_version_2, - Some(&wdk_metadata_2), - ); - - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) - .set_up_workspace_with_multiple_driver_projects( - &cwd, - Some(wdk_metadata_1), - vec![ - (workspace_member_1, package_1), - (workspace_member_2, package_2), - ], - ) - .expect_detect_wdk_build_number(25100u32) - .expect_root_manifest_exists(&cwd, true) - .expect_cargo_build(driver_name_1, &cwd.join(driver_name_1), None) - .expect_cargo_build(driver_name_2, &cwd.join(driver_name_2), None); - - let build_action = initialize_build_action( - &cwd, - profile.as_ref(), - None, - verify_signature, - sample_class, - test_build_action, - ); - assert!(build_action.is_ok()); - let run_result = build_action.expect("Failed to init build action").run(); - - assert!(matches!( - run_result.expect_err("run_result error in test: given_a_workspace_with_multiple_distinct_wdk_configurations_at_each_workspace_member_level_when_default_values_are_provided_then_wdk_metadata_parse_should_fail"), - BuildActionError::WdkMetadataParse( - TryFromCargoMetadataError::MultipleWdkConfigurationsDetected { - wdk_metadata_configurations: _ - } - ) - )); -} - -#[test] -pub fn given_a_workspace_with_multiple_distinct_wdk_configurations_at_root_and_workspace_member_level_when_default_values_are_provided_then_wdk_metadata_parse_should_fail() - { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; - let verify_signature = true; - let sample_class = false; - - // Driver project data - let driver_type_1 = "KMDF"; - let driver_name_1 = "sample-kmdf-1"; - let driver_type_2 = "UMDF"; - let driver_version_1 = "0.0.1"; - let driver_name_2 = "sample-kmdf-2"; - let driver_version_2 = "0.0.2"; - let wdk_metadata_1 = get_cargo_metadata_wdk_metadata(driver_type_1, 1, 33); - let wdk_metadata_2 = get_cargo_metadata_wdk_metadata(driver_type_2, 1, 33); - let (workspace_member_1, package_1) = get_cargo_metadata_package( - &cwd.join(driver_name_1), - driver_name_1, - driver_version_1, - Some(&wdk_metadata_1), - ); - let (workspace_member_2, package_2) = get_cargo_metadata_package( - &cwd.join(driver_name_2), - driver_name_2, - driver_version_2, - Some(&wdk_metadata_1), - ); - - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) - .set_up_workspace_with_multiple_driver_projects( - &cwd, - Some(wdk_metadata_2), - vec![ - (workspace_member_1, package_1), - (workspace_member_2, package_2), - ], - ) - .expect_root_manifest_exists(&cwd, true) - .expect_detect_wdk_build_number(25100u32) - .expect_cargo_build(driver_name_1, &cwd.join(driver_name_1), None) - .expect_cargo_build(driver_name_2, &cwd.join(driver_name_2), None); - - let build_action = initialize_build_action( - &cwd, - profile.as_ref(), - None, - verify_signature, - sample_class, - test_build_action, - ); - assert!(build_action.is_ok()); - let run_result = build_action.expect("Failed to init build action").run(); - - assert!(matches!( - run_result.expect_err("run_result error in test: given_a_workspace_with_multiple_distinct_wdk_configurations_at_root_and_workspace_member_level_when_default_values_are_provided_then_wdk_metadata_parse_should_fail"), - BuildActionError::WdkMetadataParse( - TryFromCargoMetadataError::MultipleWdkConfigurationsDetected { - wdk_metadata_configurations: _ - } - ) - )); -} - -#[test] -pub fn given_a_workspace_only_with_non_driver_projects_when_cwd_is_workspace_root_then_build_should_be_successful() - { - // Input CLI args - let cwd = PathBuf::from("C:\\tmp"); - let profile = None; - let verify_signature = true; - let sample_class = false; - - // Driver project data - let non_driver = "non-driver"; - let non_driver_version = "0.0.3"; - let (workspace_member_3, package_3) = - get_cargo_metadata_package(&cwd.join(non_driver), non_driver, non_driver_version, None); - - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) // Even when cwd is changed to driver project inside the workspace, cargo metadata read - // is going to be for the whole workspace - .set_up_workspace_with_multiple_driver_projects( - &cwd, - None, - vec![(workspace_member_3, package_3)], - ) - .expect_root_manifest_exists(&cwd, true) - .expect_detect_wdk_build_number(25100u32) - .expect_cargo_build(non_driver, &cwd.join(non_driver), None); - - assert_build_action_run_is_success( - &cwd, - profile, - None, - verify_signature, - sample_class, - test_build_action, - ); -} - -#[test] -pub fn given_a_workspace_only_with_non_driver_projects_when_cwd_is_workspace_member_then_build_should_be_successful() - { - // Input CLI args - let workspace_root_dir = PathBuf::from("C:\\tmp"); - let cwd = workspace_root_dir.join("non-driver"); - let profile = None; - let verify_signature = true; - let sample_class = false; - - // Driver project data - let non_driver = "non-driver"; - let non_driver_version = "0.0.3"; - let (workspace_member_3, package_3) = get_cargo_metadata_package( - &workspace_root_dir.join(non_driver), - non_driver, - non_driver_version, - None, - ); - - let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class) // Even when cwd is changed to driver project inside the workspace, cargo metadata read - // is going to be for the whole workspace - .set_up_workspace_with_multiple_driver_projects( - &workspace_root_dir, - None, - vec![(workspace_member_3, package_3)], - ) - .expect_root_manifest_exists(&cwd, true) - .expect_detect_wdk_build_number(25100u32) - .expect_cargo_build(non_driver, &cwd, None); - - assert_build_action_run_is_success( - &cwd, - profile, - None, - verify_signature, - sample_class, - test_build_action, - ); -} - -fn assert_build_action_run_is_success( - cwd: &PathBuf, - profile: Option, - target_arch: Option, - verify_signature: bool, - sample_class: bool, - test_build_action: &TestBuildAction, -) { - let build_action = initialize_build_action( - cwd, - profile.as_ref(), - target_arch, - verify_signature, - sample_class, - test_build_action, - ); - assert!(build_action.is_ok()); - let run_result = build_action.expect("Failed to init build action").run(); - assert!( - run_result.is_ok(), - "build action failed unexpectedly: {run_result:?}" - ); -} - -fn initialize_build_action<'a>( - cwd: &'a PathBuf, - profile: Option<&'a Profile>, - target_arch: Option, - verify_signature: bool, - sample_class: bool, - test_build_action: &'a TestBuildAction, -) -> Result, anyhow::Error> { - let sign_mode = match test_build_action.sign_mode { - SignMode::Off => SignMode::Off, - SignMode::Test { .. } => SignMode::Test { verify_signature }, - }; - BuildAction::new( +fn initialize_build_action(test_build_action: &mut TestBuildAction) -> BuildAction<'_> { + BuildAction::new_with_runners( &BuildActionParams { - working_dir: cwd, - profile, - target_arch, - sign_mode, - is_sample_class: sample_class, + working_dir: &test_build_action.cwd, + profile: test_build_action.profile.as_ref(), + target_arch: test_build_action.target_arch, + sign_mode: test_build_action.sign_mode, + is_sample_class: test_build_action.sample_class, locked: test_build_action.locked, features: &test_build_action.features, verbosity_level: clap_verbosity_flag::Verbosity::new(1, 0), }, - test_build_action.mock_wdk_build_provider(), - test_build_action.mock_run_command(), - test_build_action.mock_fs_provider(), - test_build_action.mock_metadata_provider(), + &test_build_action.mock_wdk_build_provider, + &test_build_action.mock_run_command, + &test_build_action.mock_fs_provider, + &test_build_action.mock_metadata_provider, + std::mem::take(&mut test_build_action.mock_build_task_runner), + std::mem::take(&mut test_build_action.mock_package_task_runner), ) + .expect("failed to initialize build action") } -fn get_certmgr_success_output() -> Output { - Output { - status: ExitStatus::default(), - stdout: r"==============No Certificates ========== - ==============No CTLs ========== - ==============No CRLs ========== - ============================================== - CertMgr Succeeded" - .as_bytes() - .to_vec(), - stderr: vec![], +fn default_wdk_metadata() -> TestWdkMetadata { + get_cargo_metadata_wdk_metadata("KMDF", 1, 33) +} + +fn expected_target_dir( + cwd: &Path, + target_arch: Option, + profile: Option, +) -> PathBuf { + let mut target_dir = cwd.join("target"); + if let Some(target_arch) = target_arch { + target_dir = target_dir.join(to_target_triple(target_arch)); } + target_dir.join(match profile { + Some(Profile::Release) => "release", + _ => "debug", + }) } -fn assert_build_action_run_with_env_is_success( - cwd: &PathBuf, +fn cargo_build_messages( + package_name: &str, + package_version: &str, + cwd: &Path, + target_triple: Option<&str>, profile: Option, - target_arch: Option, - verify_signature: bool, - sample_class: bool, - test_build_action: &TestBuildAction, -) { - let build_action = initialize_build_action( - cwd, - profile.as_ref(), - target_arch, - verify_signature, - sample_class, - test_build_action, - ); - assert!(build_action.is_ok()); - let run_result = run_build_action(build_action); - assert!( - run_result.is_ok(), - "build action with env failed unexpectedly: {run_result:?}" - ); +) -> Vec> { + let output = + create_cargo_build_output_json(package_name, package_version, cwd, target_triple, profile); + Message::parse_stream(io::Cursor::new(output.stdout)).collect() } -fn run_build_action( - build_action: Result, anyhow::Error>, -) -> Result<(), BuildActionError> { - let build_action = build_action.expect("Failed to init build action"); - crate::test_utils::with_env::<&str, &str, _, _>(&[], || build_action.run()) +fn build_task_error() -> super::error::BuildTaskError { + super::error::BuildTaskError::CargoBuild(CommandError::from_output( + "cargo", + &["build"], + &Output { + status: ExitStatus::from_raw(1), + stdout: vec![], + stderr: b"cargo build failed".to_vec(), + }, + )) } -/// Helper functions -//////////////////////////////////////////////////////////////////////////////// struct TestBuildAction { cwd: PathBuf, profile: Option, target_arch: Option, - sample_class: bool, sign_mode: SignMode, + sample_class: bool, locked: bool, features: Features, - - cargo_metadata: Option, - // mocks - mock_run_command: CommandExec, - mock_wdk_build_provider: WdkBuild, - mock_fs_provider: Fs, - mock_metadata_provider: MetadataProvider, + mock_run_command: MockCommandExec, + mock_wdk_build_provider: MockWdkBuild, + mock_fs_provider: MockFs, + mock_metadata_provider: MockMetadata, + mock_build_task_runner: MockBuildTaskRunner, + mock_package_task_runner: MockPackageTaskRunner, } impl TestBuildAction { @@ -1757,1347 +647,301 @@ impl TestBuildAction { cwd: PathBuf, profile: Option, target_arch: Option, + sign_mode: SignMode, sample_class: bool, ) -> Self { - let mock_run_command = CommandExec::default(); - let mock_wdk_build_provider = WdkBuild::default(); - let mock_fs_provider = Fs::default(); - let mock_metadata_provider = MetadataProvider::default(); - Self { cwd, profile, target_arch, + sign_mode, sample_class, - sign_mode: SignMode::Test { - verify_signature: false, - }, locked: false, features: Features::default(), - mock_run_command, - mock_wdk_build_provider, - mock_fs_provider, - mock_metadata_provider, - cargo_metadata: None, - } - } - - fn with_sign_mode(mut self, sign_mode: SignMode) -> Self { - self.sign_mode = sign_mode; - self - } - - fn with_locked(mut self, locked: bool) -> Self { - self.locked = locked; - self - } - - fn with_features(mut self, features: Features) -> Self { - self.features = features; - self - } - - fn set_up_standalone_driver_project( - mut self, - package_metadata: (TestMetadataWorkspaceMemberId, TestMetadataPackage), - ) -> Self { - let cargo_toml_metadata = get_cargo_metadata( - &self.cwd, - vec![package_metadata.1], - &[package_metadata.0], - None, - ); - let cargo_toml_metadata = - serde_json::from_str::(&cargo_toml_metadata) - .expect("Failed to parse cargo metadata in set_up_standalone_driver_project"); - let cargo_toml_metadata_clone = cargo_toml_metadata.clone(); - let expected_options: Vec = if self.locked { - vec!["--locked".to_string()] - } else { - vec![] - }; - let expected_features = self.features.clone(); - self.mock_metadata_provider - .expect_get_cargo_metadata_at_path() - .withf( - move |_working_dir: &Path, other_options: &Vec, features: &Features| { - *other_options == expected_options && *features == expected_features - }, - ) - .once() - .returning(move |_, _, _| Ok(cargo_toml_metadata_clone.clone())); - self.cargo_metadata = Some(cargo_toml_metadata); - self - } - - fn set_up_workspace_with_multiple_driver_projects( - mut self, - workspace_root_dir: &Path, - workspace_additional_metadata: Option, - package_metadata_list: Vec<(TestMetadataWorkspaceMemberId, TestMetadataPackage)>, - ) -> Self { - let cargo_toml_metadata = get_cargo_metadata( - workspace_root_dir, - package_metadata_list.iter().map(|p| p.1.clone()).collect(), - package_metadata_list - .into_iter() - .map(|p| p.0) - .collect::>() - .as_slice(), - workspace_additional_metadata, - ); - let cargo_toml_metadata = serde_json::from_str::( - &cargo_toml_metadata, - ) - .expect("Failed to parse cargo metadata in set_up_workspace_with_multiple_driver_projects"); - let cargo_toml_metadata_clone = cargo_toml_metadata.clone(); - let expected_options: Vec = if self.locked { - vec!["--locked".to_string()] - } else { - vec![] - }; - let expected_features = self.features.clone(); - self.mock_metadata_provider - .expect_get_cargo_metadata_at_path() - .withf( - move |_working_dir: &Path, other_options: &Vec, features: &Features| { - *other_options == expected_options && *features == expected_features - }, - ) - .once() - .returning(move |_, _, _| Ok(cargo_toml_metadata_clone.clone())); - self.cargo_metadata = Some(cargo_toml_metadata); - self - } - - fn set_up_with_custom_toml(mut self, cargo_toml_metadata: &str) -> Self { - let cargo_toml_metadata = - serde_json::from_str::(cargo_toml_metadata) - .expect("Failed to parse cargo metadata in set_up_with_custom_toml"); - let cargo_toml_metadata_clone = cargo_toml_metadata.clone(); - let expected_options: Vec = if self.locked { - vec!["--locked".to_string()] - } else { - vec![] - }; - let expected_features = self.features.clone(); - self.mock_metadata_provider - .expect_get_cargo_metadata_at_path() - .withf( - move |_working_dir: &Path, other_options: &Vec, features: &Features| { - *other_options == expected_options && *features == expected_features - }, - ) - .once() - .returning(move |_, _, _| Ok(cargo_toml_metadata_clone.clone())); - self.cargo_metadata = Some(cargo_toml_metadata); - self - } - - fn setup_target_dir(&self, dir_path: &Path) -> PathBuf { - let mut base = dir_path.join("target"); - let profile_dir_name = match self.profile { - Some(Profile::Release) => "release", - _ => "debug", - }; - if let Some(target_arch) = self.target_arch { - let triple = to_target_triple(target_arch); - base = base.join(triple); - } - base.join(profile_dir_name) - } -} - -// Presence of method ensures specific mock expectation is set -// Dir argument in any method means to operate on the relevant dir -// Output argument in any method means to override return output from default -// is_success boolean means success result of copy operation -// does_exist boolean means existence of the file or dir -// is_created boolean means whether the dir was created or not -impl TestBuildAction { - fn expect_default_build_task_steps( - self, - driver_name: &str, - cargo_build_output: Option, - ) -> Self { - let cwd = self.cwd.clone(); - self.expect_detect_wdk_build_number(25100u32) - .expect_root_manifest_exists(&cwd, true) - .expect_cargo_build(driver_name, &cwd, cargo_build_output) - } - - fn expect_default_package_task_steps( - self, - driver_name: &str, - driver_type: &str, - target_arch: CpuArchitecture, - verify_signature: bool, - ) -> Self { - let cwd = self.cwd.clone(); - let expected_certmgr_output = get_certmgr_success_output(); - let expectations = self - .expect_final_package_dir_exists(driver_name, &cwd, true) - .expect_inx_file_exists(driver_name, &cwd, true) - .expect_rename_driver_binary_dll_to_sys(driver_name, &cwd) - .expect_copy_driver_binary_sys_to_package_folder(driver_name, &cwd, true) - .expect_copy_pdb_file_to_package_folder(driver_name, &cwd, true) - .expect_copy_inx_file_to_package_folder(driver_name, &cwd, true, &cwd) - .expect_copy_map_file_to_package_folder(driver_name, &cwd, true) - .expect_stampinf(driver_name, &cwd, target_arch, None) - .expect_inf2cat(driver_name, &cwd, target_arch, None) - .expect_self_signed_cert_file_exists(&cwd, false) - .expect_certmgr_exists_check(Some(expected_certmgr_output)) - .expect_makecert(&cwd, None) - .expect_copy_self_signed_cert_file_to_package_folder(driver_name, &cwd, true) - .expect_signtool_sign_driver_binary_sys_file(driver_name, &cwd, None) - .expect_signtool_sign_cat_file(driver_name, &cwd, None) - .expect_infverif(driver_name, &cwd, driver_type, None); - if !verify_signature { - return expectations; - } - expectations - .expect_signtool_verify_driver_binary_sys_file(driver_name, &cwd, None) - .expect_signtool_verify_cat_file(driver_name, &cwd, None) - } - - /// Sets up package-task expectations for `SignMode::Off`: stampinf, - /// inf2cat, and infverif are still expected, but all certificate - /// generation, signing, and signature-verification steps are skipped. - fn expect_package_task_steps_with_sign_mode_off( - self, - driver_name: &str, - driver_type: &str, - target_arch: CpuArchitecture, - ) -> Self { - let cwd = self.cwd.clone(); - self.expect_final_package_dir_exists(driver_name, &cwd, true) - .expect_inx_file_exists(driver_name, &cwd, true) - .expect_rename_driver_binary_dll_to_sys(driver_name, &cwd) - .expect_copy_driver_binary_sys_to_package_folder(driver_name, &cwd, true) - .expect_copy_pdb_file_to_package_folder(driver_name, &cwd, true) - .expect_copy_inx_file_to_package_folder(driver_name, &cwd, true, &cwd) - .expect_copy_map_file_to_package_folder(driver_name, &cwd, true) - .expect_stampinf(driver_name, &cwd, target_arch, None) - .expect_inf2cat(driver_name, &cwd, target_arch, None) - .expect_infverif(driver_name, &cwd, driver_type, None) - } - - fn expect_default_package_task_steps_for_workspace( - self, - driver_name: &str, - driver_type: &str, - target_arch: CpuArchitecture, - verify_signature: bool, - ) -> Self { - let cwd = self.cwd.clone(); - let expected_certmgr_output = get_certmgr_success_output(); - let expectations = self - .expect_final_package_dir_exists(driver_name, &cwd, true) - .expect_inx_file_exists(driver_name, &cwd.join(driver_name), true) - .expect_rename_driver_binary_dll_to_sys(driver_name, &cwd) - .expect_copy_driver_binary_sys_to_package_folder(driver_name, &cwd, true) - .expect_copy_pdb_file_to_package_folder(driver_name, &cwd, true) - .expect_copy_inx_file_to_package_folder(driver_name, &cwd.join(driver_name), true, &cwd) - .expect_copy_map_file_to_package_folder(driver_name, &cwd, true) - .expect_stampinf(driver_name, &cwd, target_arch, None) - .expect_inf2cat(driver_name, &cwd, target_arch, None) - .expect_self_signed_cert_file_exists(&cwd, false) - .expect_certmgr_exists_check(Some(expected_certmgr_output)) - .expect_makecert(&cwd, None) - .expect_copy_self_signed_cert_file_to_package_folder(driver_name, &cwd, true) - .expect_signtool_sign_driver_binary_sys_file(driver_name, &cwd, None) - .expect_signtool_sign_cat_file(driver_name, &cwd, None) - .expect_infverif(driver_name, &cwd, driver_type, None); - if !verify_signature { - return expectations; - } - expectations - .expect_signtool_verify_driver_binary_sys_file(driver_name, &cwd, None) - .expect_signtool_verify_cat_file(driver_name, &cwd, None) - } - - fn expect_root_manifest_exists(mut self, root_dir: &Path, does_exist: bool) -> Self { - self.mock_fs_provider - .expect_exists() - .with(eq(root_dir.join("Cargo.toml"))) - .once() - .returning(move |_| does_exist); - self - } - - fn expect_self_signed_cert_file_exists(mut self, driver_dir: &Path, does_exist: bool) -> Self { - let expected_target_dir = self.setup_target_dir(driver_dir); - let expected_src_driver_cert_path = expected_target_dir.join("WDRLocalTestCert.cer"); - self.mock_fs_provider - .expect_exists() - .with(eq(expected_src_driver_cert_path)) - .once() - .returning(move |_| does_exist); - self - } - - fn expect_final_package_dir_exists( - mut self, - driver_name: &str, - cwd: &Path, - does_exist: bool, - ) -> Self { - let expected_driver_name_underscored = driver_name.replace('-', "_"); - let expected_target_dir = self.setup_target_dir(cwd); - let expected_final_package_dir_path = - expected_target_dir.join(format!("{expected_driver_name_underscored}_package")); - self.mock_fs_provider - .expect_exists() - .with(eq(expected_final_package_dir_path)) - .once() - .returning(move |_| does_exist); - self - } - - fn expect_dir_created(mut self, driver_name: &str, cwd: &Path, created: bool) -> Self { - let expected_driver_name_underscored = driver_name.replace('-', "_"); - let expected_target_dir = self.setup_target_dir(cwd); - let expected_final_package_dir_path = - expected_target_dir.join(format!("{expected_driver_name_underscored}_package")); - self.mock_fs_provider - .expect_create_dir() - .with(eq(expected_final_package_dir_path.clone())) - .once() - .returning(move |_| { - if created { - Ok(()) - } else { - Err(FileError::CreateDirError( - expected_final_package_dir_path.clone(), - std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "create error"), - )) - } - }); - self - } - - fn expect_cargo_build( - mut self, - driver_name: &str, - cwd: &Path, - override_output: Option, - ) -> Self { - // cargo build on the package - let expected_cargo_command: &'static str = "cargo"; - let manifest_path = cwd - .join("Cargo.toml") - .to_string_lossy() - .trim_start_matches("\\\\?\\") - .to_string(); - let mut expected_cargo_build_args: Vec = vec![ - "build", - "--message-format=json-render-diagnostics", - "-p", - &driver_name, - "--manifest-path", - &manifest_path, - ] - .into_iter() - .map(ToString::to_string) - .collect(); - if let Some(profile) = self.profile { - expected_cargo_build_args.push("--profile".to_string()); - expected_cargo_build_args.push(profile.to_string()); - } - - if let Some(target_arch) = self.target_arch { - expected_cargo_build_args.push("--target".to_string()); - expected_cargo_build_args.push(to_target_triple(target_arch)); - } - - if self.locked { - expected_cargo_build_args.push("--locked".to_string()); - } - - expected_cargo_build_args.push("-v".to_string()); - let expected_output = override_output.unwrap_or_else(|| Output { - status: ExitStatus::default(), - stdout: vec![], - stderr: vec![], - }); - self.mock_run_command - .expect_run() - .withf( - move |command: &str, - args: &[&str], - _env_vars: &Option<&HashMap<&str, &str>>, - _working_dir: &Option<&Path>| - -> bool { - command == expected_cargo_command && args == expected_cargo_build_args - }, - ) - .once() - .returning(move |_, _, _, _| Ok(expected_output.clone())); - self - } - - fn expect_probe_target_arch_using_cargo_rustc( - mut self, - driver_dir: &Path, - detected_arch: CpuArchitecture, - override_output: Option, - ) -> Self { - if self.target_arch.is_some() { - println!("`cargo rustc` must not be probed when target architecture is already set"); - return self; - } - let expected_working_dir = driver_dir.to_path_buf(); - let arch_str = match detected_arch { - CpuArchitecture::Amd64 => "x86_64", - CpuArchitecture::Arm64 => "aarch64", - }; - let mut expected_args: Vec = vec!["rustc".to_string()]; - if self.locked { - expected_args.push("--locked".to_string()); - } - expected_args.push("--".to_string()); - expected_args.push("--print".to_string()); - expected_args.push("cfg".to_string()); - let expected_args_for_err = expected_args.clone(); - self.mock_run_command - .expect_run() - .withf( - move |command: &str, - args: &[&str], - _env_vars: &Option<&HashMap<&str, &str>>, - working_dir: &Option<&Path>| { - command == "cargo" - && args == expected_args - && working_dir.is_some_and(|d| d == expected_working_dir.as_path()) - }, - ) - .once() - .returning(move |_, _, _, _| match override_output.clone() { - Some(output) => { - if output.status.code() == Some(0) { - Ok(Output { - status: ExitStatus::from_raw(0), - stdout: vec![], - stderr: vec![], - }) - } else { - let err_args: Vec<&str> = - expected_args_for_err.iter().map(String::as_str).collect(); - Err(CommandError::from_output("cargo", &err_args, &output)) - } - } - None => Ok(Output { - status: ExitStatus::default(), - stdout: format!("target_arch=\"{arch_str}\"\n").as_bytes().to_vec(), - stderr: vec![], - }), - }); - self - } - - fn expect_inx_file_exists( - mut self, - driver_name: &str, - driver_dir: &Path, - does_exist: bool, - ) -> Self { - let expected_driver_name_underscored = driver_name.replace('-', "_"); - let expected_inx_file_path = - driver_dir.join(format!("{expected_driver_name_underscored}.inx")); - self.mock_fs_provider - .expect_exists() - .with(eq(expected_inx_file_path)) - .once() - .returning(move |_| does_exist); - self - } - - fn expect_rename_driver_binary_dll_to_sys( - mut self, - driver_name: &str, - driver_dir: &Path, - ) -> Self { - let expected_driver_name_underscored = driver_name.replace('-', "_"); - let expected_target_dir = self.setup_target_dir(driver_dir); - let expected_src_driver_dll_path = - expected_target_dir.join(format!("{expected_driver_name_underscored}.dll")); - let expected_src_driver_sys_path = - expected_target_dir.join(format!("{expected_driver_name_underscored}.sys")); - self.mock_fs_provider - .expect_rename() - .with( - eq(expected_src_driver_dll_path), - eq(expected_src_driver_sys_path), - ) - .once() - .returning(|_, _| Ok(())); - self - } - - fn expect_copy_driver_binary_sys_to_package_folder( - mut self, - driver_name: &str, - driver_dir: &Path, - is_success: bool, - ) -> Self { - let expected_driver_name_underscored = driver_name.replace('-', "_"); - let expected_target_dir = self.setup_target_dir(driver_dir); - let expected_final_package_dir_path = - expected_target_dir.join(format!("{expected_driver_name_underscored}_package")); - let mock_non_zero_bytes_copied_size = 1000u64; - - let expected_src_driver_sys_path = - expected_target_dir.join(format!("{expected_driver_name_underscored}.sys")); - let expected_dest_driver_binary_path = - expected_final_package_dir_path.join(format!("{expected_driver_name_underscored}.sys")); - let expected_src_driver_binary_path = expected_src_driver_sys_path; - self.mock_fs_provider - .expect_copy() - .with( - eq(expected_src_driver_binary_path.clone()), - eq(expected_dest_driver_binary_path.clone()), - ) - .once() - .returning(move |_, _| { - if is_success { - Ok(mock_non_zero_bytes_copied_size) - } else { - Err(FileError::CopyError( - expected_src_driver_binary_path.clone(), - expected_dest_driver_binary_path.clone(), - std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "copy error"), - )) - } - }); - self - } - - fn expect_copy_pdb_file_to_package_folder( - mut self, - driver_name: &str, - driver_dir: &Path, - is_success: bool, - ) -> Self { - let expected_driver_name_underscored = driver_name.replace('-', "_"); - let expected_target_dir = self.setup_target_dir(driver_dir); - let expected_final_package_dir_path = - expected_target_dir.join(format!("{expected_driver_name_underscored}_package")); - let mock_non_zero_bytes_copied_size = 1000u64; - - // copy pdb file to package directory - let expected_src_driver_pdb_path = - expected_target_dir.join(format!("{expected_driver_name_underscored}.pdb")); - let expected_dest_driver_pdb_path = - expected_final_package_dir_path.join(format!("{expected_driver_name_underscored}.pdb")); - self.mock_fs_provider - .expect_copy() - .with( - eq(expected_src_driver_pdb_path.clone()), - eq(expected_dest_driver_pdb_path.clone()), - ) - .once() - .returning(move |_, _| { - if is_success { - Ok(mock_non_zero_bytes_copied_size) - } else { - Err(FileError::CopyError( - expected_src_driver_pdb_path.clone(), - expected_dest_driver_pdb_path.clone(), - std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "copy error"), - )) - } - }); - self - } - - fn expect_copy_inx_file_to_package_folder( - mut self, - driver_name: &str, - driver_dir: &Path, - is_success: bool, - workspace_root_dir: &Path, - ) -> Self { - let expected_driver_name_underscored = driver_name.replace('-', "_"); - let expected_target_dir = self.setup_target_dir(workspace_root_dir); - let expected_final_package_dir_path = - expected_target_dir.join(format!("{expected_driver_name_underscored}_package")); - let mock_non_zero_bytes_copied_size = 1000u64; - - // copy inx file to package directory - let expected_src_driver_inx_path = - driver_dir.join(format!("{expected_driver_name_underscored}.inx")); - let expected_dest_driver_inf_path = - expected_final_package_dir_path.join(format!("{expected_driver_name_underscored}.inf")); - self.mock_fs_provider - .expect_copy() - .with( - eq(expected_src_driver_inx_path.clone()), - eq(expected_dest_driver_inf_path.clone()), - ) - .once() - .returning(move |_, _| { - if is_success { - Ok(mock_non_zero_bytes_copied_size) - } else { - Err(FileError::CopyError( - expected_src_driver_inx_path.clone(), - expected_dest_driver_inf_path.clone(), - std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "copy error"), - )) - } - }); - self - } - - fn expect_copy_map_file_to_package_folder( - mut self, - driver_name: &str, - driver_dir: &Path, - is_success: bool, - ) -> Self { - let expected_driver_name_underscored = driver_name.replace('-', "_"); - let expected_target_dir = self.setup_target_dir(driver_dir); - let expected_final_package_dir_path = - expected_target_dir.join(format!("{expected_driver_name_underscored}_package")); - let mock_non_zero_bytes_copied_size = 1000u64; - - // copy map file to package directory - let expected_src_driver_map_path = expected_target_dir - .join("deps") - .join(format!("{expected_driver_name_underscored}.map")); - let expected_dest_driver_map_path = - expected_final_package_dir_path.join(format!("{expected_driver_name_underscored}.map")); - self.mock_fs_provider - .expect_copy() - .with( - eq(expected_src_driver_map_path.clone()), - eq(expected_dest_driver_map_path.clone()), - ) - .once() - .returning(move |_, _| { - if is_success { - Ok(mock_non_zero_bytes_copied_size) - } else { - Err(FileError::CopyError( - expected_src_driver_map_path.clone(), - expected_dest_driver_map_path.clone(), - std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "copy error"), - )) - } - }); - self + mock_run_command: MockCommandExec::new(), + mock_wdk_build_provider: MockWdkBuild::new(), + mock_fs_provider: MockFs::new(), + mock_metadata_provider: MockMetadata::new(), + mock_build_task_runner: MockBuildTaskRunner::new(), + mock_package_task_runner: MockPackageTaskRunner::new(), + } } - fn expect_copy_self_signed_cert_file_to_package_folder( + fn set_up_standalone_driver_project( mut self, - driver_name: &str, - driver_dir: &Path, - is_success: bool, + package_name: &str, + metadata: Option, ) -> Self { - let expected_driver_name_underscored = driver_name.replace('-', "_"); - let expected_target_dir = self.setup_target_dir(driver_dir); - let expected_final_package_dir_path = - expected_target_dir.join(format!("{expected_driver_name_underscored}_package")); - let mock_non_zero_bytes_copied_size = 1000u64; - - // copy self signed certificate to package directory - let expected_src_cert_file_path = expected_target_dir.join("WDRLocalTestCert.cer"); - let expected_dest_driver_cert_path = - expected_final_package_dir_path.join("WDRLocalTestCert.cer"); - self.mock_fs_provider - .expect_copy() - .with( - eq(expected_src_cert_file_path.clone()), - eq(expected_dest_driver_cert_path.clone()), - ) + let is_driver = metadata.is_some(); + let cargo_toml_metadata = metadata_from_packages( + &self.cwd, + vec![( + package_name, + self.cwd.clone(), + metadata, + if is_driver { "cdylib" } else { "lib" }, + if is_driver { "cdylib" } else { "lib" }, + if is_driver { "main.rs" } else { "lib.rs" }, + )], + ); + let cargo_toml_metadata_for_closure = cargo_toml_metadata; + self.mock_metadata_provider + .expect_get_cargo_metadata_at_path() .once() - .returning(move |_, _| { - if is_success { - Ok(mock_non_zero_bytes_copied_size) - } else { - Err(FileError::CopyError( - expected_src_cert_file_path.clone(), - expected_dest_driver_cert_path.clone(), - std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "copy error"), - )) - } - }); + .returning(move |_, _, _| Ok(cargo_toml_metadata_for_closure.clone())); self } - fn expect_stampinf( + fn set_up_workspace_with_multiple_driver_projects( mut self, - driver_name: &str, - driver_dir: &Path, - target_arch: CpuArchitecture, - override_output: Option, + workspace_root_dir: &Path, + packages: Vec>, ) -> Self { - // Run stampinf command - let expected_driver_name_underscored = driver_name.replace('-', "_"); - let expected_target_dir = self.setup_target_dir(driver_dir); - let expected_final_package_dir_path = - expected_target_dir.join(format!("{expected_driver_name_underscored}_package")); - let expected_dest_driver_inf_path = - expected_final_package_dir_path.join(format!("{expected_driver_name_underscored}.inf")); - - let expected_stampinf_command: &'static str = "stampinf"; - let wdk_metadata = Wdk::try_from( - self.cargo_metadata - .as_ref() - .expect("cargo metadata must be available"), - ) - .expect("Wdk metadata must be available"); - - if let DriverConfig::Kmdf(kmdf_config) = wdk_metadata.driver_model { - let expected_cat_file_name = format!("{expected_driver_name_underscored}.cat"); - let expected_stampinf_args: Vec = vec![ - "-f".to_string(), - expected_dest_driver_inf_path.to_string_lossy().to_string(), - "-d".to_string(), - "*".to_string(), - "-a".to_string(), - target_arch.to_string(), - "-c".to_string(), - expected_cat_file_name, - "-v".to_string(), - "*".to_string(), - "-k".to_string(), - format!( - "{}.{}", - kmdf_config.kmdf_version_major, kmdf_config.target_kmdf_version_minor - ), - ]; - - self.mock_run_command - .expect_run() - .withf( - move |command: &str, - args: &[&str], - _env_vars: &Option<&HashMap<&str, &str>>, - _working_dir: &Option<&Path>| - -> bool { - println!("command: {command}, args: {args:?}"); - println!( - "expected_command: {expected_stampinf_command}, expected_args: \ - {expected_stampinf_args:?}" - ); - command == expected_stampinf_command && args == expected_stampinf_args - }, - ) - .once() - .returning(move |_, _, _, _| match override_output.clone() { - Some(output) => match output.status.code() { - Some(0) => Ok(Output { - status: ExitStatus::from_raw(0), - stdout: vec![], - stderr: vec![], - }), - _ => Err(CommandError::from_output("stampinf", &[], &output)), - }, - None => Ok(Output { - status: ExitStatus::default(), - stdout: vec![], - stderr: vec![], - }), - }); - } + let cargo_toml_metadata = metadata_from_packages(workspace_root_dir, packages); + let cargo_toml_metadata_for_closure = cargo_toml_metadata; + self.mock_metadata_provider + .expect_get_cargo_metadata_at_path() + .once() + .returning(move |_, _, _| Ok(cargo_toml_metadata_for_closure.clone())); self } - fn expect_inf2cat( - mut self, - driver_name: &str, - driver_dir: &Path, - target_arch: CpuArchitecture, - override_output: Option, - ) -> Self { - // Run inf2cat command - let expected_driver_name_underscored = driver_name.replace('-', "_"); - let expected_target_dir = self.setup_target_dir(driver_dir); - let expected_final_package_dir_path = - expected_target_dir.join(format!("{expected_driver_name_underscored}_package")); - - let expected_inf2cat_command: &'static str = "inf2cat"; - - let expected_inf2cat_arg = match target_arch { - CpuArchitecture::Amd64 => "10_x64", - CpuArchitecture::Arm64 => "Server10_arm64", - }; - let expected_inf2cat_args: Vec = vec![ - format!( - "/driver:{}", - expected_final_package_dir_path.to_string_lossy() - ), - format!("/os:{}", expected_inf2cat_arg), - "/uselocaltime".to_string(), - ]; - - self.mock_run_command - .expect_run() - .withf( - move |command: &str, - args: &[&str], - _env_vars: &Option<&HashMap<&str, &str>>, - _working_dir: &Option<&Path>| - -> bool { - println!("command: {command}, args: {args:?}"); - println!( - "expected_command: {expected_inf2cat_command}, expected_args: \ - {expected_inf2cat_args:?}" - ); - command == expected_inf2cat_command && args == expected_inf2cat_args - }, - ) + fn set_up_with_custom_toml(mut self, cargo_toml_metadata: &str) -> Self { + let cargo_toml_metadata = serde_json::from_str::(cargo_toml_metadata) + .expect("failed to parse cargo metadata"); + let cargo_toml_metadata_for_closure = cargo_toml_metadata; + self.mock_metadata_provider + .expect_get_cargo_metadata_at_path() .once() - .returning(move |_, _, _, _| match override_output.clone() { - Some(output) => match output.status.code() { - Some(0) => Ok(Output { - status: ExitStatus::from_raw(0), - stdout: vec![], - stderr: vec![], - }), - _ => Err(CommandError::from_output("inf2cat", &[], &output)), - }, - None => Ok(Output { - status: ExitStatus::default(), - stdout: vec![], - stderr: vec![], - }), - }); + .returning(move |_, _, _| Ok(cargo_toml_metadata_for_closure.clone())); self } - fn expect_certmgr_exists_check(mut self, override_output: Option) -> Self { - // check for cert in cert store using certmgr - let expected_certmgr_command: &'static str = "certmgr.exe"; - let expected_certmgr_args: Vec = - vec!["-s".to_string(), "WDRTestCertStore".to_string()]; - self.mock_run_command - .expect_run() - .withf( - move |command: &str, - args: &[&str], - _env_vars: &Option<&HashMap<&str, &str>>, - _working_dir: &Option<&Path>| - -> bool { - command == expected_certmgr_command && args == expected_certmgr_args - }, - ) - .returning(move |_, _, _, _| match override_output.clone() { - Some(output) => match output.status.code() { - Some(0) => Ok(Output { - status: ExitStatus::from_raw(0), - stdout: output.stdout, - stderr: output.stderr, - }), - _ => Err(CommandError::from_output("certmgr", &[], &output)), - }, - None => Ok(Output { - status: ExitStatus::default(), - stdout: vec![], - stderr: vec![], - }), + fn expect_metadata_for_paths(&mut self, metadata_by_path: Vec<(PathBuf, CargoMetadata)>) { + self.mock_metadata_provider + .expect_get_cargo_metadata_at_path() + .times(metadata_by_path.len()) + .returning(move |path, _other_options, _features| { + let requested_path = PathBuf::from(path); + metadata_by_path + .iter() + .find_map(|(expected_path, metadata)| { + (requested_path == *expected_path).then_some(metadata.clone()) + }) + .ok_or_else(|| { + cargo_metadata::Error::from(io::Error::new( + io::ErrorKind::NotFound, + format!("unexpected metadata path: {}", requested_path.display()), + )) + }) }); - self } - fn expect_certmgr_create_cert_from_store( - mut self, - driver_dir: &Path, - override_output: Option, - ) -> Self { - // create cert from store using certmgr - let expected_target_dir = self.setup_target_dir(driver_dir); - let expected_self_signed_cert_file_path = expected_target_dir.join("WDRLocalTestCert.cer"); - - let expected_certmgr_command: &'static str = "certmgr.exe"; - let expected_certmgr_args: Vec = vec![ - "-put".to_string(), - "-s".to_string(), - "WDRTestCertStore".to_string(), - "-c".to_string(), - "-n".to_string(), - "WDRLocalTestCert".to_string(), - expected_self_signed_cert_file_path - .to_string_lossy() - .to_string(), - ]; - self.mock_run_command - .expect_run() - .withf( - move |command: &str, - args: &[&str], - _env_vars: &Option<&HashMap<&str, &str>>, - _working_dir: &Option<&Path>| - -> bool { - command == expected_certmgr_command && args == expected_certmgr_args - }, - ) + fn expect_detect_wdk_build_number(&mut self, build_number: u32) { + self.mock_wdk_build_provider + .expect_detect_wdk_build_number() .once() - .returning(move |_, _, _, _| match override_output.clone() { - Some(output) => match output.status.code() { - Some(0) => Ok(Output { - status: ExitStatus::from_raw(0), - stdout: vec![], - stderr: vec![], - }), - _ => Err(CommandError::from_output("certmgr", &[], &output)), - }, - None => Ok(Output { - status: ExitStatus::default(), - stdout: vec![], - stderr: vec![], - }), - }); - self + .returning(move || Ok(build_number)); } - fn expect_makecert(mut self, driver_dir: &Path, override_output: Option) -> Self { - // create self signed certificate using makecert - let expected_target_dir = self.setup_target_dir(driver_dir); - let expected_makecert_command: &'static str = "makecert"; - let expected_src_driver_cert_path = expected_target_dir.join("WDRLocalTestCert.cer"); - let expected_makecert_args: Vec = vec![ - "-r".to_string(), - "-pe".to_string(), - "-a".to_string(), - "SHA256".to_string(), - "-eku".to_string(), - "1.3.6.1.5.5.7.3.3".to_string(), - "-ss".to_string(), - "WDRTestCertStore".to_string(), - "-n".to_string(), - "CN=WDRLocalTestCert".to_string(), - expected_src_driver_cert_path.to_string_lossy().to_string(), - ]; - - self.mock_run_command - .expect_run() - .withf( - move |command: &str, - args: &[&str], - _env_vars: &Option<&HashMap<&str, &str>>, - _working_dir: &Option<&Path>| - -> bool { - command == expected_makecert_command && args == expected_makecert_args - }, - ) + fn expect_root_manifest_exists(&mut self, root_dir: &Path, exists: bool) { + let cargo_toml_path = root_dir.join("Cargo.toml"); + self.mock_fs_provider + .expect_exists() + .with(eq(cargo_toml_path)) .once() - .returning(move |_, _, _, _| match override_output.clone() { - Some(output) => match output.status.code() { - Some(0) => Ok(Output { - status: ExitStatus::from_raw(0), - stdout: vec![], - stderr: vec![], - }), - _ => Err(CommandError::from_output("makecert", &[], &output)), - }, - None => Ok(Output { - status: ExitStatus::default(), - stdout: vec![], - stderr: vec![], - }), - }); - self + .returning(move |_| exists); } - fn expect_signtool_sign_driver_binary_sys_file( - mut self, - driver_name: &str, - driver_dir: &Path, - override_output: Option, - ) -> Self { - let expected_driver_name_underscored = driver_name.replace('-', "_"); - let expected_target_dir = self.setup_target_dir(driver_dir); - let expected_final_package_dir_path = - expected_target_dir.join(format!("{expected_driver_name_underscored}_package")); - let expected_signtool_command: &'static str = "signtool"; - - // sign driver binary using signtool - let expected_dest_driver_binary_path = - expected_final_package_dir_path.join(format!("{expected_driver_name_underscored}.sys")); - let expected_signtool_args: Vec = vec![ - "sign".to_string(), - "/v".to_string(), - "/s".to_string(), - "WDRTestCertStore".to_string(), - "/n".to_string(), - "WDRLocalTestCert".to_string(), - "/t".to_string(), - "http://timestamp.digicert.com".to_string(), - "/fd".to_string(), - "SHA256".to_string(), - expected_dest_driver_binary_path - .to_string_lossy() - .to_string(), - ]; - - self.mock_run_command - .expect_run() - .withf( - move |command: &str, - args: &[&str], - _env_vars: &Option<&HashMap<&str, &str>>, - _working_dir: &Option<&Path>| - -> bool { - command == expected_signtool_command && args == expected_signtool_args - }, - ) + fn expect_read_dir_entries(&mut self, root_dir: &Path) { + let root_dir = root_dir.to_path_buf(); + self.mock_fs_provider + .expect_read_dir_entries() + .with(eq(root_dir.clone())) .once() - .returning(move |_, _, _, _| match override_output.clone() { - Some(output) => match output.status.code() { - Some(0) => Ok(Output { - status: ExitStatus::from_raw(0), - stdout: vec![], - stderr: vec![], - }), - _ => Err(CommandError::from_output("signtool", &[], &output)), - }, - None => Ok(Output { - status: ExitStatus::default(), - stdout: vec![], - stderr: vec![], - }), + .returning(move |_| { + let mut entries: Vec = fs::read_dir(&root_dir) + .map_err(|e| FileError::ReadDirError(root_dir.clone(), e))? + .map(|entry_res| { + let entry = entry_res + .map_err(|e| FileError::ReadDirEntriesError(root_dir.clone(), e))?; + let entry_path = entry.path(); + let is_dir = entry + .file_type() + .map_err(|e| FileError::DirFileTypeError(entry_path.clone(), e))? + .is_dir(); + Ok(crate::providers::fs::DirEntryInfo { + path: entry_path, + is_dir, + }) + }) + .collect::, _>>()?; + entries.sort_by(|a, b| a.path.cmp(&b.path)); + Ok(entries) }); - self } - fn expect_signtool_sign_cat_file( - mut self, - driver_name: &str, - driver_dir: &Path, - override_output: Option, - ) -> Self { - let expected_driver_name_underscored = driver_name.replace('-', "_"); - let expected_target_dir = self.setup_target_dir(driver_dir); - let expected_final_package_dir_path = - expected_target_dir.join(format!("{expected_driver_name_underscored}_package")); - let expected_signtool_command: &'static str = "signtool"; - - // sign driver cat file using signtool - let expected_dest_driver_cat_file_path = - expected_final_package_dir_path.join(format!("{expected_driver_name_underscored}.cat")); - let expected_signtool_args: Vec = vec![ - "sign".to_string(), - "/v".to_string(), - "/s".to_string(), - "WDRTestCertStore".to_string(), - "/n".to_string(), - "WDRLocalTestCert".to_string(), - "/t".to_string(), - "http://timestamp.digicert.com".to_string(), - "/fd".to_string(), - "SHA256".to_string(), - expected_dest_driver_cat_file_path - .to_string_lossy() - .to_string(), - ]; - self.mock_run_command - .expect_run() - .withf( - move |command: &str, - args: &[&str], - _env_vars: &Option<&HashMap<&str, &str>>, - _working_dir: &Option<&Path>| - -> bool { - command == expected_signtool_command && args == expected_signtool_args - }, - ) + fn expect_dir_cargo_toml_exists(&mut self, dir: &Path, exists: bool) { + let cargo_toml_path = dir.join("Cargo.toml"); + self.mock_fs_provider + .expect_exists() + .with(eq(cargo_toml_path)) .once() - .returning(move |_, _, _, _| match override_output.clone() { - Some(output) => match output.status.code() { - Some(0) => Ok(Output { - status: ExitStatus::from_raw(0), - stdout: vec![], - stderr: vec![], - }), - _ => Err(CommandError::from_output("signtool", &[], &output)), - }, - None => Ok(Output { - status: ExitStatus::default(), - stdout: vec![], - stderr: vec![], - }), - }); - self + .returning(move |_| exists); } - fn expect_signtool_verify_driver_binary_sys_file( - mut self, - driver_name: &str, - driver_dir: &Path, - override_output: Option, - ) -> Self { - let expected_driver_name_underscored = driver_name.replace('-', "_"); - let expected_target_dir = self.setup_target_dir(driver_dir); - let expected_final_package_dir_path = - expected_target_dir.join(format!("{expected_driver_name_underscored}_package")); - let expected_signtool_command: &'static str = "signtool"; - - // verify signed driver binary using signtool - let expected_dest_driver_binary_path = - expected_final_package_dir_path.join(format!("{expected_driver_name_underscored}.sys")); - let expected_signtool_verify_args: Vec = vec![ - "verify".to_string(), - "/v".to_string(), - "/pa".to_string(), - expected_dest_driver_binary_path - .to_string_lossy() - .to_string(), - ]; - self.mock_run_command + fn expect_build_runner( + &mut self, + package_name: &str, + working_dir: &Path, + profile: Option, + target_arch: Option, + result: Result>, super::error::BuildTaskError>, + ) { + let expected_package_name = package_name.to_string(); + let expected_working_dir = working_dir.to_path_buf(); + let expected_profile = profile; + let expected_target_arch = target_arch; + let expected_locked = self.locked; + let expected_features = self.features.clone(); + self.mock_build_task_runner .expect_run() - .withf( - move |command: &str, - args: &[&str], - _env_vars: &Option<&HashMap<&str, &str>>, - _working_dir: &Option<&Path>| - -> bool { - command == expected_signtool_command && args == expected_signtool_verify_args - }, - ) + .withf(move |params: &BuildTaskParams<'_>, _command_exec| { + params.package_name == expected_package_name + && params.working_dir == expected_working_dir + && params.profile.copied() == expected_profile + && params.target_arch == expected_target_arch + && params.locked == expected_locked + && params.features.all_features == expected_features.all_features + && params.features.no_default_features == expected_features.no_default_features + && params.features.features == expected_features.features + }) .once() - .returning(move |_, _, _, _| match override_output.clone() { - Some(output) => match output.status.code() { - Some(0) => Ok(Output { - status: ExitStatus::from_raw(0), - stdout: vec![], - stderr: vec![], - }), - _ => Err(CommandError::from_output("signtool", &[], &output)), - }, - None => Ok(Output { - status: ExitStatus::default(), - stdout: vec![], - stderr: vec![], - }), - }); - self + .return_once(move |_, _| result); } - fn expect_signtool_verify_cat_file( - mut self, - driver_name: &str, - driver_dir: &Path, - override_output: Option, - ) -> Self { - let expected_driver_name_underscored = driver_name.replace('-', "_"); - let expected_target_dir = self.setup_target_dir(driver_dir); - let expected_final_package_dir_path = - expected_target_dir.join(format!("{expected_driver_name_underscored}_package")); - let expected_signtool_command: &'static str = "signtool"; - - // verify signed driver cat file using signtool - let expected_dest_driver_cat_file_path = - expected_final_package_dir_path.join(format!("{expected_driver_name_underscored}.cat")); - let expected_signtool_verify_args: Vec = vec![ - "verify".to_string(), - "/v".to_string(), - "/pa".to_string(), - expected_dest_driver_cat_file_path - .to_string_lossy() - .to_string(), - ]; - self.mock_run_command + fn expect_package_runner( + &mut self, + package_name: &str, + working_dir: &Path, + target_dir: &Path, + target_arch: CpuArchitecture, + sign_mode: SignMode, + sample_class: bool, + ) { + let expected_package_name = package_name.to_string(); + let expected_working_dir = working_dir.to_path_buf(); + let expected_target_dir = target_dir.to_path_buf(); + self.mock_package_task_runner .expect_run() - .withf( - move |command: &str, - args: &[&str], - _env_vars: &Option<&HashMap<&str, &str>>, - _working_dir: &Option<&Path>| - -> bool { - command == expected_signtool_command && args == expected_signtool_verify_args - }, - ) - .once() - .returning(move |_, _, _, _| match override_output.clone() { - Some(output) => match output.status.code() { - Some(0) => Ok(Output { - status: ExitStatus::from_raw(0), - stdout: vec![], - stderr: vec![], - }), - _ => Err(CommandError::from_output("stampinf", &[], &output)), - }, - None => Ok(Output { - status: ExitStatus::default(), - stdout: vec![], - stderr: vec![], - }), - }); - self - } - - fn expect_detect_wdk_build_number(mut self, expected_wdk_build_number: u32) -> Self { - self.mock_wdk_build_provider - .expect_detect_wdk_build_number() + .withf(move |params: &PackageTaskParams<'_>, _, _, _| { + params.package_name == expected_package_name + && params.working_dir == expected_working_dir + && params.target_dir == expected_target_dir + && *params.target_arch == target_arch + && params.sign_mode == sign_mode + && params.sample_class == sample_class + && matches!(params.driver_model, DriverConfig::Kmdf(_)) + }) .once() - .returning(move || Ok(expected_wdk_build_number)); - self + .return_once(|_, _, _, _| Ok(())); } - fn expect_infverif( - mut self, - driver_name: &str, - driver_dir: &Path, - driver_type: &str, - override_output: Option, - ) -> Self { - let mut expected_infverif_args = vec!["/v".to_string()]; - if driver_type.eq_ignore_ascii_case("KMDF") || driver_type.eq_ignore_ascii_case("WDM") { - expected_infverif_args.push("/w".to_string()); - } else { - expected_infverif_args.push("/u".to_string()); - } - if self.sample_class { - expected_infverif_args.push("/msft".to_string()); - } - let expected_infverif_command: &'static str = "infverif"; - let expected_driver_name_underscored = driver_name.replace('-', "_"); - let expected_target_dir = self.setup_target_dir(driver_dir); - let expected_final_package_dir_path = - expected_target_dir.join(format!("{expected_driver_name_underscored}_package")); - let expected_dest_inf_file_path = - expected_final_package_dir_path.join(format!("{expected_driver_name_underscored}.inf")); - expected_infverif_args.push(expected_dest_inf_file_path.to_string_lossy().to_string()); - + fn expect_probe_target_arch_using_cargo_rustc( + &mut self, + working_dir: &Path, + detected_arch: CpuArchitecture, + ) { + let expected_working_dir = working_dir.to_path_buf(); + let arch_str = match detected_arch { + CpuArchitecture::Amd64 => "x86_64", + CpuArchitecture::Arm64 => "aarch64", + }; self.mock_run_command .expect_run() .withf( move |command: &str, args: &[&str], _env_vars: &Option<&HashMap<&str, &str>>, - _working_dir: &Option<&Path>| - -> bool { - command == expected_infverif_command && args == expected_infverif_args + working_dir: &Option<&Path>| { + command == "cargo" + && args == ["rustc", "--", "--print", "cfg"] + && working_dir.is_some_and(|dir| dir == expected_working_dir.as_path()) }, ) .once() - .returning(move |_, _, _, _| match override_output.clone() { - Some(output) => match output.status.code() { - Some(0) => Ok(Output { - status: ExitStatus::from_raw(0), - stdout: vec![], - stderr: vec![], - }), - _ => Err(CommandError::from_output("infverif", &[], &output)), - }, - None => Ok(Output { + .returning(move |_, _, _, _| { + Ok(Output { status: ExitStatus::default(), - stdout: vec![], + stdout: format!("target_arch=\"{arch_str}\"\n").into_bytes(), stderr: vec![], - }), + }) }); - self - } - - const fn mock_wdk_build_provider(&self) -> &WdkBuild { - &self.mock_wdk_build_provider } +} - const fn mock_run_command(&self) -> &CommandExec { - &self.mock_run_command - } +struct TestWorkspaceRoot { + root: PathBuf, +} - const fn mock_fs_provider(&self) -> &Fs { - &self.mock_fs_provider +impl TestWorkspaceRoot { + fn new(prefix: &str) -> Self { + static NEXT_ID: AtomicU64 = AtomicU64::new(0); + let unique_id = NEXT_ID.fetch_add(1, Ordering::Relaxed); + let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("target") + .join("test-artifacts") + .join(format!("{prefix}-{unique_id}")); + if root.exists() { + fs::remove_dir_all(&root).expect("failed to remove stale test workspace"); + } + fs::create_dir_all(&root).expect("failed to create test workspace"); + Self { root } } +} - const fn mock_metadata_provider(&self) -> &MetadataProvider { - &self.mock_metadata_provider +impl Drop for TestWorkspaceRoot { + fn drop(&mut self) { + if self.root.exists() { + let _ = fs::remove_dir_all(&self.root); + } } } -fn invalid_driver_cargo_toml() -> String { - r#" - { - "packages": [ - { - "name": "sample-driver", - "version": "0.0.1", - "id": "path+file:///C:/tmp/sample-driver#0.0.1", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": null, - "source": null, - "dependencies": [], - "targets": [ - { - "kind": [ - "cdylib" - ], - "crate_types": [ - "cdylib" - ], - "name": "sample_driver", - "src_path": "C:\\tmp\\sample-driver\\src\\lib.rs", - "edition": "2021", - "doc": true, - "doctest": false, - "test": false - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "C:\\tmp\\sample-driver\\build.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [], - "nightly": [ - "wdk/nightly", - "wdk-sys/nightly" - ] - }, - "manifest_path": "C:\\tmp\\sample-driver\\Cargo.toml", - "metadata": { - "wdk": {} - }, - "publish": [], - "authors": [], - "categories": [], - "keywords": [], - "readme": null, - "repository": null, - "homepage": null, - "documentation": null, - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": null - } - ], - "workspace_members": [ - "path+file:///C:/tmp/sample-driver#0.0.1" - ], - "target_directory": "C:\\tmp\\sample-driver\\target", - "version": 1, - "workspace_root": "C:\\tmp\\sample-driver", - "metadata": { - "wdk": { - "driver-model": { - "driver-type": "KMDF" - } - } - } - } - "# - .to_string() +fn metadata_from_packages( + workspace_root_dir: &Path, + packages: Vec>, +) -> CargoMetadata { + let mut package_json = Vec::new(); + let mut workspace_member_ids = Vec::new(); + for (package_name, package_dir, metadata, target_kind, crate_type, source_file) in packages { + let (workspace_member_id, package) = get_cargo_metadata_package_with_target( + &package_dir, + package_name, + DEFAULT_DRIVER_VERSION, + metadata, + target_kind, + crate_type, + source_file, + ); + workspace_member_ids.push(workspace_member_id); + package_json.push(package); + } + serde_json::from_str::(&get_cargo_metadata( + workspace_root_dir, + package_json, + &workspace_member_ids, + None, + )) + .expect("failed to parse cargo metadata") } #[derive(Clone)] struct TestMetadataPackage(String); + #[derive(Clone)] struct TestMetadataWorkspaceMemberId(String); + #[derive(Clone)] struct TestWdkMetadata(String); @@ -3130,7 +974,6 @@ fn get_cargo_metadata( .map(|p| p.0) .collect::>() .join(", "), - // Require quotes around each member workspace_member_list .iter() .map(|s| format!("\"{}\"", s.0)) @@ -3140,25 +983,19 @@ fn get_cargo_metadata( ) } -fn get_cargo_metadata_package( +fn get_cargo_metadata_package_with_target( root_dir: &Path, - default_package_name: &str, - default_package_version: &str, - metadata: Option<&TestWdkMetadata>, + package_name: &str, + package_version: &str, + metadata: Option, + target_kind: &str, + crate_type: &str, + source_file: &str, ) -> (TestMetadataWorkspaceMemberId, TestMetadataPackage) { let normalized_root = root_dir.to_string_lossy().replace('\\', "/"); let normalized_root = normalized_root.trim_start_matches("//?/"); - let package_id = - format!("path+file:///{normalized_root}#{default_package_name}@{default_package_version}"); - let (metadata_section, has_metadata) = metadata.map_or_else( - || (String::from("null"), false), - |metadata| ((metadata.0).clone(), true), - ); - let (target_kind, crate_type, source_file) = if has_metadata { - ("cdylib", "cdylib", "main.rs") - } else { - ("lib", "lib", "lib.rs") - }; + let package_id = format!("path+file:///{normalized_root}#{package_name}@{package_version}"); + let metadata_section = metadata.map_or_else(|| String::from("null"), |metadata| metadata.0); let manifest_path = root_dir .join("Cargo.toml") .to_string_lossy() @@ -3175,8 +1012,8 @@ fn get_cargo_metadata_package( TestMetadataPackage(format!( r#" {{ - "name": "{default_package_name}", - "version": "{default_package_version}", + "name": "{package_name}", + "version": "{package_version}", "id": "{package_id}", "dependencies": [], "targets": [ @@ -3187,7 +1024,7 @@ fn get_cargo_metadata_package( "crate_types": [ "{crate_type}" ], - "name": "{default_package_name}", + "name": "{package_name}", "src_path": "{source_path}", "edition": "2021", "doc": true, @@ -3233,9 +1070,6 @@ fn get_cargo_metadata_wdk_metadata( )) } -/// Creates a valid cargo compiler-artifact JSON message for testing. -/// This simulates the JSON output that `cargo build --message-format=json` -/// produces. fn create_cargo_build_output_json( package_name: &str, package_version: &str, @@ -3260,7 +1094,6 @@ fn strip_windows_extended_prefix(path: &Path) -> String { return path_str.into_owned(); }; - // Handle UNC paths without_prefix.strip_prefix("UNC\\").map_or_else( || without_prefix.to_string(), |unc_rest| format!(r"\\{unc_rest}"), @@ -3277,22 +1110,16 @@ fn create_cargo_build_output_json_with_manifest( is_driver: bool, ) -> Output { let normalized_name = package_name.replace('-', "_"); - - // Determine profile directory name let profile_dir = match profile { Some(Profile::Release) => "release", _ => "debug", }; - - // For non-driver projects, use "lib" instead of "cdylib" to ensure BuildTask - // returns DllNotFound let (kind, crate_types, file_ext) = if is_driver { ("cdylib", "cdylib", "dll") } else { ("lib", "lib", "rlib") }; - // Build the artifact path based on target_triple and profile let mut artifact_path = workspace_root.join("target"); if let Some(target) = target_triple { artifact_path = artifact_path.join(target); @@ -3306,7 +1133,6 @@ fn create_cargo_build_output_json_with_manifest( let package_id = format!("path+file:///{package_dir}#{package_name}@{package_version}"); let manifest_path = strip_windows_extended_prefix(manifest_path); let artifact_path = strip_windows_extended_prefix(&artifact_path); - let pdb_path = Path::new(&artifact_path) .with_extension("pdb") .to_string_lossy() @@ -3361,11 +1187,14 @@ mod get_target_dir_from_output { fn unparsable_output_fails() { let workspace_root_dir = PathBuf::from(r"C:\tmp\sample-kmdf"); let wdk_metadata = super::get_cargo_metadata_wdk_metadata("KMDF", 1, 0); - let (_workspace_member, package_json) = super::get_cargo_metadata_package( + let (_workspace_member, package_json) = super::get_cargo_metadata_package_with_target( &workspace_root_dir, "sample-kmdf", "0.0.1", - Some(&wdk_metadata), + Some(wdk_metadata), + "cdylib", + "cdylib", + "main.rs", ); let package = serde_json::from_str::(&package_json.0) .expect("Failed to parse package json"); @@ -3389,11 +1218,14 @@ mod get_target_dir_from_output { fn no_matching_artifact_fails() { let workspace_root_dir = PathBuf::from(r"C:\tmp\sample-kmdf"); let wdk_metadata = super::get_cargo_metadata_wdk_metadata("KMDF", 1, 0); - let (_workspace_member, package_json) = super::get_cargo_metadata_package( + let (_workspace_member, package_json) = super::get_cargo_metadata_package_with_target( &workspace_root_dir, "sample-kmdf", "0.0.1", - Some(&wdk_metadata), + Some(wdk_metadata), + "cdylib", + "cdylib", + "main.rs", ); let package = serde_json::from_str::(&package_json.0) .expect("Failed to parse package json"); @@ -3424,11 +1256,14 @@ mod get_target_dir_from_output { fn matching_artifact_without_dll_fails() { let workspace_root_dir = PathBuf::from(r"C:\tmp\sample-kmdf"); let wdk_metadata = super::get_cargo_metadata_wdk_metadata("KMDF", 1, 0); - let (_workspace_member, package_json) = super::get_cargo_metadata_package( + let (_workspace_member, package_json) = super::get_cargo_metadata_package_with_target( &workspace_root_dir, "sample-kmdf", "0.0.1", - Some(&wdk_metadata), + Some(wdk_metadata), + "cdylib", + "cdylib", + "main.rs", ); let package = serde_json::from_str::(&package_json.0) .expect("Failed to parse package json"); @@ -3474,11 +1309,14 @@ mod get_target_dir_from_output { fn matching_dll_resolves_target_dir() { let workspace_root_dir = PathBuf::from(r"C:\tmp\sample-kmdf"); let wdk_metadata = super::get_cargo_metadata_wdk_metadata("KMDF", 1, 0); - let (_workspace_member, package_json) = super::get_cargo_metadata_package( + let (_workspace_member, package_json) = super::get_cargo_metadata_package_with_target( &workspace_root_dir, "sample-kmdf", "0.0.1", - Some(&wdk_metadata), + Some(wdk_metadata), + "cdylib", + "cdylib", + "main.rs", ); let package = serde_json::from_str::(&package_json.0) .expect("Failed to parse package json"); @@ -3525,20 +1363,24 @@ mod get_target_arch_from_cargo_rustc { process::{ExitStatus, Output}, }; - use clap_cargo::Features; use wdk_build::CpuArchitecture; - use super::{super::features_to_cargo_args, BuildActionError, TestBuildAction}; + use super::{BuildActionError, SignMode, TestBuildAction, initialize_build_action}; fn run_parse_test(cfg_output: Vec, expected_arch: CpuArchitecture) { let cwd = PathBuf::from(r"C:\tmp"); - let mut test_build_action = TestBuildAction::new(cwd.clone(), None, None, false); + let mut test_build_action = TestBuildAction::new( + cwd.clone(), + None, + None, + SignMode::Test { + verify_signature: false, + }, + false, + ); expect_cargo_rustc_print_cfg(&mut test_build_action, cwd.clone(), cfg_output); - let build_action = - super::initialize_build_action(&cwd, None, None, true, false, &test_build_action) - .expect("Failed to init build action"); - + let build_action = initialize_build_action(&mut test_build_action); let arch = build_action .get_target_arch_from_cargo_rustc(&cwd) .expect("Expected target arch to be detected"); @@ -3567,43 +1409,24 @@ mod get_target_arch_from_cargo_rustc { } #[test] - fn parses_target_when_features_specified() { + fn unsupported_arch_returns_error() { let cwd = PathBuf::from(r"C:\tmp"); - let mut features = Features::default(); - features.no_default_features = true; - features.features = vec!["foo".to_string(), "bar".to_string()]; - let mut test_build_action = - TestBuildAction::new(cwd.clone(), None, None, false).with_features(features); - expect_cargo_rustc_print_cfg( - &mut test_build_action, + let mut test_build_action = TestBuildAction::new( cwd.clone(), - b"target_arch=\"x86_64\"\n".to_vec(), + None, + None, + SignMode::Test { + verify_signature: false, + }, + false, ); - - let build_action = - super::initialize_build_action(&cwd, None, None, true, false, &test_build_action) - .expect("Failed to init build action"); - - let arch = build_action - .get_target_arch_from_cargo_rustc(&cwd) - .expect("Expected target arch to be detected"); - assert_eq!(arch, CpuArchitecture::Amd64); - } - - #[test] - fn unsupported_arch_returns_error() { - let cwd = PathBuf::from(r"C:\tmp"); - let mut test_build_action = TestBuildAction::new(cwd.clone(), None, None, false); expect_cargo_rustc_print_cfg( &mut test_build_action, cwd.clone(), b"target_arch=\"mips\"\n".to_vec(), ); - let build_action = - super::initialize_build_action(&cwd, None, None, true, false, &test_build_action) - .expect("Failed to init build action"); - + let build_action = initialize_build_action(&mut test_build_action); let err = build_action .get_target_arch_from_cargo_rustc(&cwd) .expect_err("Expected UnsupportedArchitecture error"); @@ -3613,17 +1436,22 @@ mod get_target_arch_from_cargo_rustc { #[test] fn missing_target_arch_returns_error() { let cwd = PathBuf::from(r"C:\tmp"); - let mut test_build_action = TestBuildAction::new(cwd.clone(), None, None, false); + let mut test_build_action = TestBuildAction::new( + cwd.clone(), + None, + None, + SignMode::Test { + verify_signature: false, + }, + false, + ); expect_cargo_rustc_print_cfg( &mut test_build_action, cwd.clone(), b"some_other_cfg=\"value\"\n".to_vec(), ); - let build_action = - super::initialize_build_action(&cwd, None, None, true, false, &test_build_action) - .expect("Failed to init build action"); - + let build_action = initialize_build_action(&mut test_build_action); let err = build_action .get_target_arch_from_cargo_rustc(&cwd) .expect_err("Expected CannotDetectTargetArch error"); @@ -3633,13 +1461,18 @@ mod get_target_arch_from_cargo_rustc { #[test] fn invalid_utf8_returns_error() { let cwd = PathBuf::from(r"C:\tmp"); - let mut test_build_action = TestBuildAction::new(cwd.clone(), None, None, false); + let mut test_build_action = TestBuildAction::new( + cwd.clone(), + None, + None, + SignMode::Test { + verify_signature: false, + }, + false, + ); expect_cargo_rustc_print_cfg(&mut test_build_action, cwd.clone(), vec![0xFF, 0xFE]); - let build_action = - super::initialize_build_action(&cwd, None, None, true, false, &test_build_action) - .expect("Failed to init build action"); - + let build_action = initialize_build_action(&mut test_build_action); let err = build_action .get_target_arch_from_cargo_rustc(&cwd) .expect_err("Expected CannotDetectTargetArch error"); @@ -3651,12 +1484,6 @@ mod get_target_arch_from_cargo_rustc { cwd: PathBuf, stdout: Vec, ) { - let mut expected_args: Vec = vec!["rustc".to_string()]; - if test_build_action.locked { - expected_args.push("--locked".to_string()); - } - expected_args.extend(features_to_cargo_args(&test_build_action.features)); - expected_args.extend(["--", "--print", "cfg"].map(String::from)); test_build_action .mock_run_command .expect_run() @@ -3664,10 +1491,9 @@ mod get_target_arch_from_cargo_rustc { move |command: &str, args: &[&str], _env_vars: &Option<&HashMap<&str, &str>>, - working_dir: &Option<&Path>| - -> bool { + working_dir: &Option<&Path>| { command == "cargo" - && args == expected_args + && args == ["rustc", "--", "--print", "cfg"] && matches!(working_dir, Some(dir) if *dir == cwd.as_path()) }, ) From 0bde3585990f7a7b358fe188af3e940024d63254 Mon Sep 17 00:00:00 2001 From: krishnakumar4a4 Date: Wed, 15 Jul 2026 09:52:52 +0530 Subject: [PATCH 02/12] test: serialize test execution to prevent non-deterministic failures due to expectation overrides --- .../src/actions/build/package_task.rs | 18 +++++++++++++++ crates/cargo-wdk/src/actions/build/tests.rs | 22 +++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/crates/cargo-wdk/src/actions/build/package_task.rs b/crates/cargo-wdk/src/actions/build/package_task.rs index 943ee7173..eacdffb35 100644 --- a/crates/cargo-wdk/src/actions/build/package_task.rs +++ b/crates/cargo-wdk/src/actions/build/package_task.rs @@ -677,6 +677,7 @@ mod tests { os::windows::process::ExitStatusExt, path::PathBuf, process::{ExitStatus, Output}, + sync::Mutex, }; use mockall::predicate::eq; @@ -690,6 +691,14 @@ mod tests { wdk_build::MockWdkBuild, }; + // Serializes test execution within this module. The + // `stampinf_version_overrides_with_env_var` test mutates the + // `STAMPINF_VERSION` env var via `with_env`, which is process-global state. + // Other tests that call `run_stampinf()` read the same env var, so parallel + // execution causes non-deterministic failures when the env var is + // unexpectedly set. + static TEST_MUTEX: Mutex<()> = Mutex::new(()); + #[test] fn new_succeeds_for_valid_args() { let package_name = "test_package"; @@ -816,6 +825,7 @@ mod tests { #[test] fn run_packages_driver_with_expected_operations() { + let _lock = TEST_MUTEX.lock().unwrap(); let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); @@ -866,6 +876,7 @@ mod tests { #[test] fn run_verifies_signatures_when_enabled() { + let _lock = TEST_MUTEX.lock().unwrap(); let mut harness = PackageTaskHarness::new().with_sign_mode(SignMode::Test { verify_signature: true, }); @@ -887,6 +898,7 @@ mod tests { #[test] fn run_exports_certificate_from_store_when_it_already_exists() { + let _lock = TEST_MUTEX.lock().unwrap(); let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); @@ -935,6 +947,7 @@ mod tests { #[test] fn run_returns_error_when_inx_file_is_missing() { + let _lock = TEST_MUTEX.lock().unwrap(); let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); harness.expect_exists(paths.src_inx_file_path.clone(), false); @@ -951,6 +964,7 @@ mod tests { #[test] fn run_returns_error_when_copying_driver_binary_fails() { + let _lock = TEST_MUTEX.lock().unwrap(); let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); @@ -985,6 +999,7 @@ mod tests { #[test] fn run_returns_error_when_stampinf_fails() { + let _lock = TEST_MUTEX.lock().unwrap(); let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); @@ -1022,6 +1037,7 @@ mod tests { #[test] fn run_returns_error_when_inf2cat_fails() { + let _lock = TEST_MUTEX.lock().unwrap(); let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); @@ -1060,6 +1076,7 @@ mod tests { #[test] fn run_skips_infverif_for_samples_when_wdk_build_is_in_bugged_range() { + let _lock = TEST_MUTEX.lock().unwrap(); let mut harness = PackageTaskHarness::new().with_sample_class(true); let paths = harness.paths(); @@ -1109,6 +1126,7 @@ mod tests { #[test] fn stampinf_version_overrides_with_env_var() { + let _lock = TEST_MUTEX.lock().unwrap(); // verify both with and without the env var set scenarios let scenarios = [ ("env_set", Some("1.2.3.4"), true), diff --git a/crates/cargo-wdk/src/actions/build/tests.rs b/crates/cargo-wdk/src/actions/build/tests.rs index 36c1b8ef3..416cf82ef 100644 --- a/crates/cargo-wdk/src/actions/build/tests.rs +++ b/crates/cargo-wdk/src/actions/build/tests.rs @@ -857,6 +857,12 @@ impl TestBuildAction { CpuArchitecture::Amd64 => "x86_64", CpuArchitecture::Arm64 => "aarch64", }; + let mut expected_args: Vec = vec!["rustc".to_string()]; + if self.locked { + expected_args.push("--locked".to_string()); + } + expected_args.extend(super::features_to_cargo_args(&self.features)); + expected_args.extend(["--".to_string(), "--print".to_string(), "cfg".to_string()]); self.mock_run_command .expect_run() .withf( @@ -864,8 +870,10 @@ impl TestBuildAction { args: &[&str], _env_vars: &Option<&HashMap<&str, &str>>, working_dir: &Option<&Path>| { + let expected_refs: Vec<&str> = + expected_args.iter().map(String::as_str).collect(); command == "cargo" - && args == ["rustc", "--", "--print", "cfg"] + && args == expected_refs.as_slice() && working_dir.is_some_and(|dir| dir == expected_working_dir.as_path()) }, ) @@ -1484,6 +1492,14 @@ mod get_target_arch_from_cargo_rustc { cwd: PathBuf, stdout: Vec, ) { + let mut expected_args: Vec = vec!["rustc".to_string()]; + if test_build_action.locked { + expected_args.push("--locked".to_string()); + } + expected_args.extend(super::super::features_to_cargo_args( + &test_build_action.features, + )); + expected_args.extend(["--".to_string(), "--print".to_string(), "cfg".to_string()]); test_build_action .mock_run_command .expect_run() @@ -1492,8 +1508,10 @@ mod get_target_arch_from_cargo_rustc { args: &[&str], _env_vars: &Option<&HashMap<&str, &str>>, working_dir: &Option<&Path>| { + let expected_refs: Vec<&str> = + expected_args.iter().map(String::as_str).collect(); command == "cargo" - && args == ["rustc", "--", "--print", "cfg"] + && args == expected_refs.as_slice() && matches!(working_dir, Some(dir) if *dir == cwd.as_path()) }, ) From db1ab5560aa84f1a5b0fe1d3310aa9b7635263cd Mon Sep 17 00:00:00 2001 From: krishnakumar4a4 Date: Wed, 15 Jul 2026 16:32:52 +0530 Subject: [PATCH 03/12] refactor: organize build action tests into submodules Group top-level orchestration tests into three submodules by scenario type: - standalone_driver_project: single-package driver builds - driver_workspace: multi-package Cargo workspace builds - emulated_workspace: filesystem-based emulated workspace builds No test logic changes; only structural reorganization for clarity. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: fa978dd1-9d61-4cea-a03a-7ebc914228fa --- crates/cargo-wdk/src/actions/build/tests.rs | 904 ++++++++++---------- 1 file changed, 461 insertions(+), 443 deletions(-) diff --git a/crates/cargo-wdk/src/actions/build/tests.rs b/crates/cargo-wdk/src/actions/build/tests.rs index 416cf82ef..8585aa4e2 100644 --- a/crates/cargo-wdk/src/actions/build/tests.rs +++ b/crates/cargo-wdk/src/actions/build/tests.rs @@ -49,164 +49,178 @@ type PackageSpec<'a> = ( &'a str, ); -#[test] -fn given_a_driver_project_when_target_arch_is_detected_then_build_action_orchestrates_build_and_package() - { - let cwd = PathBuf::from(r"C:\tmp\sample-driver"); - let target_dir = expected_target_dir(&cwd, None, None); - let mut test_build_action = TestBuildAction::new( - cwd.clone(), - None, - None, - SignMode::Test { - verify_signature: false, - }, - false, - ) - .set_up_standalone_driver_project(DEFAULT_DRIVER_NAME, Some(default_wdk_metadata())); +mod standalone_driver_project { + use super::*; - test_build_action.expect_build_runner( - DEFAULT_DRIVER_NAME, - &cwd, - None, - None, - Ok(cargo_build_messages( + #[test] + fn given_a_driver_project_when_target_arch_is_detected_then_build_action_orchestrates_build_and_package() + { + let cwd = PathBuf::from(r"C:\tmp\sample-driver"); + let target_dir = expected_target_dir(&cwd, None, None); + let mut test_build_action = TestBuildAction::new( + cwd.clone(), + None, + None, + SignMode::Test { + verify_signature: false, + }, + false, + ) + .set_up_standalone_driver_project(DEFAULT_DRIVER_NAME, Some(default_wdk_metadata())); + + test_build_action.expect_build_runner( DEFAULT_DRIVER_NAME, - DEFAULT_DRIVER_VERSION, &cwd, None, None, - )), - ); - test_build_action.expect_probe_target_arch_using_cargo_rustc(&cwd, CpuArchitecture::Amd64); - test_build_action.expect_package_runner( - DEFAULT_DRIVER_NAME, - &cwd, - &target_dir, - CpuArchitecture::Amd64, - SignMode::Test { - verify_signature: false, - }, - false, - ); + Ok(cargo_build_messages( + DEFAULT_DRIVER_NAME, + DEFAULT_DRIVER_VERSION, + &cwd, + None, + None, + )), + ); + test_build_action.expect_probe_target_arch_using_cargo_rustc(&cwd, CpuArchitecture::Amd64); + test_build_action.expect_package_runner( + DEFAULT_DRIVER_NAME, + &cwd, + &target_dir, + CpuArchitecture::Amd64, + SignMode::Test { + verify_signature: false, + }, + false, + ); - let build_action = initialize_build_action(&mut test_build_action); - let result = build_action.run_from_workspace_root(&cwd); + let build_action = initialize_build_action(&mut test_build_action); + let result = build_action.run_from_workspace_root(&cwd); - assert!( - result.is_ok(), - "build action failed unexpectedly: {result:?}" - ); -} + assert!( + result.is_ok(), + "build action failed unexpectedly: {result:?}" + ); + } -#[test] -fn given_explicit_target_arch_when_building_then_probe_is_skipped_and_package_runner_uses_it() { - let cwd = PathBuf::from(r"C:\tmp\sample-driver"); - let profile = Some(Profile::Release); - let target_arch = CpuArchitecture::Arm64; - let target_dir = expected_target_dir(&cwd, Some(target_arch), profile); - let target_triple = to_target_triple(target_arch); - let mut test_build_action = TestBuildAction::new( - cwd.clone(), - profile, - Some(target_arch), - SignMode::Test { - verify_signature: true, - }, - true, - ) - .set_up_standalone_driver_project(DEFAULT_DRIVER_NAME, Some(default_wdk_metadata())); + #[test] + fn given_explicit_target_arch_when_building_then_probe_is_skipped_and_package_runner_uses_it() { + let cwd = PathBuf::from(r"C:\tmp\sample-driver"); + let profile = Some(Profile::Release); + let target_arch = CpuArchitecture::Arm64; + let target_dir = expected_target_dir(&cwd, Some(target_arch), profile); + let target_triple = to_target_triple(target_arch); + let mut test_build_action = TestBuildAction::new( + cwd.clone(), + profile, + Some(target_arch), + SignMode::Test { + verify_signature: true, + }, + true, + ) + .set_up_standalone_driver_project(DEFAULT_DRIVER_NAME, Some(default_wdk_metadata())); - test_build_action.expect_build_runner( - DEFAULT_DRIVER_NAME, - &cwd, - profile, - Some(target_arch), - Ok(cargo_build_messages( + test_build_action.expect_build_runner( DEFAULT_DRIVER_NAME, - DEFAULT_DRIVER_VERSION, &cwd, - Some(target_triple.as_str()), profile, - )), - ); - test_build_action.expect_package_runner( - DEFAULT_DRIVER_NAME, - &cwd, - &target_dir, - target_arch, - SignMode::Test { - verify_signature: true, - }, - true, - ); + Some(target_arch), + Ok(cargo_build_messages( + DEFAULT_DRIVER_NAME, + DEFAULT_DRIVER_VERSION, + &cwd, + Some(target_triple.as_str()), + profile, + )), + ); + test_build_action.expect_package_runner( + DEFAULT_DRIVER_NAME, + &cwd, + &target_dir, + target_arch, + SignMode::Test { + verify_signature: true, + }, + true, + ); - let build_action = initialize_build_action(&mut test_build_action); - let result = build_action.run_from_workspace_root(&cwd); + let build_action = initialize_build_action(&mut test_build_action); + let result = build_action.run_from_workspace_root(&cwd); - assert!( - result.is_ok(), - "build action failed unexpectedly: {result:?}" - ); -} + assert!( + result.is_ok(), + "build action failed unexpectedly: {result:?}" + ); + } -#[test] -fn given_a_non_driver_package_when_building_then_package_runner_is_skipped() { - let cwd = PathBuf::from(r"C:\tmp\non-driver"); - let mut test_build_action = TestBuildAction::new( - cwd.clone(), - None, - None, - SignMode::Test { - verify_signature: false, - }, - false, - ) - .set_up_standalone_driver_project(DEFAULT_DRIVER_NAME, None); + #[test] + fn given_a_non_driver_package_when_building_then_package_runner_is_skipped() { + let cwd = PathBuf::from(r"C:\tmp\non-driver"); + let mut test_build_action = TestBuildAction::new( + cwd.clone(), + None, + None, + SignMode::Test { + verify_signature: false, + }, + false, + ) + .set_up_standalone_driver_project(DEFAULT_DRIVER_NAME, None); - test_build_action.expect_build_runner( - DEFAULT_DRIVER_NAME, - &cwd, - None, - None, - Ok(cargo_build_messages( + test_build_action.expect_build_runner( DEFAULT_DRIVER_NAME, - DEFAULT_DRIVER_VERSION, &cwd, None, None, - )), - ); - test_build_action - .mock_package_task_runner - .expect_run() - .never(); - - let build_action = initialize_build_action(&mut test_build_action); - let result = build_action.run_from_workspace_root(&cwd); - - assert!( - result.is_ok(), - "build action failed unexpectedly: {result:?}" - ); -} + Ok(cargo_build_messages( + DEFAULT_DRIVER_NAME, + DEFAULT_DRIVER_VERSION, + &cwd, + None, + None, + )), + ); + test_build_action + .mock_package_task_runner + .expect_run() + .never(); -#[test] -fn given_a_driver_package_without_cdylib_when_building_then_package_runner_is_skipped() { - let cwd = PathBuf::from(r"C:\tmp\driver-lib"); - let mut test_build_action = TestBuildAction::new( - cwd.clone(), - None, - None, - SignMode::Test { - verify_signature: false, - }, - false, - ) - .set_up_with_custom_toml(&get_cargo_metadata( - &cwd, - vec![ - get_cargo_metadata_package_with_target( + let build_action = initialize_build_action(&mut test_build_action); + let result = build_action.run_from_workspace_root(&cwd); + + assert!( + result.is_ok(), + "build action failed unexpectedly: {result:?}" + ); + } + + #[test] + fn given_a_driver_package_without_cdylib_when_building_then_package_runner_is_skipped() { + let cwd = PathBuf::from(r"C:\tmp\driver-lib"); + let mut test_build_action = TestBuildAction::new( + cwd.clone(), + None, + None, + SignMode::Test { + verify_signature: false, + }, + false, + ) + .set_up_with_custom_toml(&get_cargo_metadata( + &cwd, + vec![ + get_cargo_metadata_package_with_target( + &cwd, + DEFAULT_DRIVER_NAME, + DEFAULT_DRIVER_VERSION, + Some(default_wdk_metadata()), + "lib", + "lib", + "lib.rs", + ) + .1, + ], + &[get_cargo_metadata_package_with_target( &cwd, DEFAULT_DRIVER_NAME, DEFAULT_DRIVER_VERSION, @@ -215,350 +229,354 @@ fn given_a_driver_package_without_cdylib_when_building_then_package_runner_is_sk "lib", "lib.rs", ) - .1, - ], - &[get_cargo_metadata_package_with_target( - &cwd, - DEFAULT_DRIVER_NAME, - DEFAULT_DRIVER_VERSION, - Some(default_wdk_metadata()), - "lib", - "lib", - "lib.rs", - ) - .0], - None, - )); + .0], + None, + )); - test_build_action.expect_build_runner(DEFAULT_DRIVER_NAME, &cwd, None, None, Ok(Vec::new())); - test_build_action - .mock_package_task_runner - .expect_run() - .never(); + test_build_action.expect_build_runner( + DEFAULT_DRIVER_NAME, + &cwd, + None, + None, + Ok(Vec::new()), + ); + test_build_action + .mock_package_task_runner + .expect_run() + .never(); - let build_action = initialize_build_action(&mut test_build_action); - let result = build_action.run_from_workspace_root(&cwd); + let build_action = initialize_build_action(&mut test_build_action); + let result = build_action.run_from_workspace_root(&cwd); - assert!( - result.is_ok(), - "build action failed unexpectedly: {result:?}" - ); + assert!( + result.is_ok(), + "build action failed unexpectedly: {result:?}" + ); + } } -#[test] -fn given_a_workspace_root_when_one_member_fails_then_workspace_error_is_returned() { - let workspace_root = PathBuf::from(r"C:\tmp\workspace"); - let driver_name_1 = "sample-kmdf-1"; - let driver_name_2 = "sample-kmdf-2"; - let driver_dir_1 = workspace_root.join(driver_name_1); - let driver_dir_2 = workspace_root.join(driver_name_2); - let target_arch = CpuArchitecture::Amd64; - let target_dir_1 = expected_target_dir(&driver_dir_1, Some(target_arch), None); - let target_triple = to_target_triple(target_arch); - let mut test_build_action = TestBuildAction::new( - workspace_root.clone(), - None, - Some(target_arch), - SignMode::Test { - verify_signature: false, - }, - false, - ) - .set_up_workspace_with_multiple_driver_projects( - &workspace_root, - vec![ - ( - driver_name_1, - driver_dir_1.clone(), - Some(default_wdk_metadata()), - "cdylib", - "cdylib", - "main.rs", - ), - ( - driver_name_2, - driver_dir_2.clone(), - Some(default_wdk_metadata()), - "cdylib", - "cdylib", - "main.rs", - ), - ], - ); +mod driver_workspace { + use super::*; - test_build_action.expect_build_runner( - driver_name_1, - &driver_dir_1, - None, - Some(target_arch), - Ok(cargo_build_messages( + #[test] + fn given_a_workspace_root_when_one_member_fails_then_workspace_error_is_returned() { + let workspace_root = PathBuf::from(r"C:\tmp\workspace"); + let driver_name_1 = "sample-kmdf-1"; + let driver_name_2 = "sample-kmdf-2"; + let driver_dir_1 = workspace_root.join(driver_name_1); + let driver_dir_2 = workspace_root.join(driver_name_2); + let target_arch = CpuArchitecture::Amd64; + let target_dir_1 = expected_target_dir(&driver_dir_1, Some(target_arch), None); + let target_triple = to_target_triple(target_arch); + let mut test_build_action = TestBuildAction::new( + workspace_root.clone(), + None, + Some(target_arch), + SignMode::Test { + verify_signature: false, + }, + false, + ) + .set_up_workspace_with_multiple_driver_projects( + &workspace_root, + vec![ + ( + driver_name_1, + driver_dir_1.clone(), + Some(default_wdk_metadata()), + "cdylib", + "cdylib", + "main.rs", + ), + ( + driver_name_2, + driver_dir_2.clone(), + Some(default_wdk_metadata()), + "cdylib", + "cdylib", + "main.rs", + ), + ], + ); + + test_build_action.expect_build_runner( driver_name_1, - DEFAULT_DRIVER_VERSION, &driver_dir_1, - Some(target_triple.as_str()), None, - )), - ); - test_build_action.expect_build_runner( - driver_name_2, - &driver_dir_2, - None, - Some(target_arch), - Err(build_task_error()), - ); - test_build_action.expect_package_runner( - driver_name_1, - &driver_dir_1, - &target_dir_1, - target_arch, - SignMode::Test { - verify_signature: false, - }, - false, - ); - - let build_action = initialize_build_action(&mut test_build_action); - let result = build_action.run_from_workspace_root(&workspace_root); - - assert!(matches!( - result, - Err(BuildActionError::OneOrMoreWorkspaceMembersFailedToBuild(path)) - if path == workspace_root - )); -} - -#[test] -fn given_a_workspace_member_directory_when_building_then_only_that_member_is_orchestrated() { - let workspace_root = PathBuf::from(r"C:\tmp\workspace"); - let driver_name_1 = "sample-kmdf-1"; - let driver_name_2 = "sample-kmdf-2"; - let driver_dir_1 = workspace_root.join(driver_name_1); - let driver_dir_2 = workspace_root.join(driver_name_2); - let target_arch = CpuArchitecture::Amd64; - let target_dir_2 = expected_target_dir(&driver_dir_2, Some(target_arch), None); - let target_triple = to_target_triple(target_arch); - let mut test_build_action = TestBuildAction::new( - driver_dir_2.clone(), - None, - Some(target_arch), - SignMode::Test { - verify_signature: false, - }, - false, - ) - .set_up_workspace_with_multiple_driver_projects( - &workspace_root, - vec![ - ( + Some(target_arch), + Ok(cargo_build_messages( driver_name_1, - driver_dir_1, - Some(default_wdk_metadata()), - "cdylib", - "cdylib", - "main.rs", - ), - ( - driver_name_2, - driver_dir_2.clone(), - Some(default_wdk_metadata()), - "cdylib", - "cdylib", - "main.rs", - ), - ], - ); - - test_build_action.expect_build_runner( - driver_name_2, - &driver_dir_2, - None, - Some(target_arch), - Ok(cargo_build_messages( + DEFAULT_DRIVER_VERSION, + &driver_dir_1, + Some(target_triple.as_str()), + None, + )), + ); + test_build_action.expect_build_runner( driver_name_2, - DEFAULT_DRIVER_VERSION, &driver_dir_2, - Some(target_triple.as_str()), None, - )), - ); - test_build_action.expect_package_runner( - driver_name_2, - &driver_dir_2, - &target_dir_2, - target_arch, - SignMode::Test { - verify_signature: false, - }, - false, - ); + Some(target_arch), + Err(build_task_error()), + ); + test_build_action.expect_package_runner( + driver_name_1, + &driver_dir_1, + &target_dir_1, + target_arch, + SignMode::Test { + verify_signature: false, + }, + false, + ); - let build_action = initialize_build_action(&mut test_build_action); - let result = build_action.run_from_workspace_root(&driver_dir_2); + let build_action = initialize_build_action(&mut test_build_action); + let result = build_action.run_from_workspace_root(&workspace_root); - assert!( - result.is_ok(), - "build action failed unexpectedly: {result:?}" - ); -} + assert!(matches!( + result, + Err(BuildActionError::OneOrMoreWorkspaceMembersFailedToBuild(path)) + if path == workspace_root + )); + } -#[test] -fn given_an_emulated_workspace_when_running_then_each_valid_project_is_built() { - let emulated_workspace = TestWorkspaceRoot::new("build-action-emulated-workspace"); - let driver_name_1 = "driver-a"; - let driver_name_2 = "driver-b"; - let ignored_dir = "docs"; - let driver_dir_1 = emulated_workspace.root.join(driver_name_1); - let driver_dir_2 = emulated_workspace.root.join(driver_name_2); - let ignored_dir_path = emulated_workspace.root.join(ignored_dir); - fs::create_dir_all(&driver_dir_1).expect("failed to create driver-a directory"); - fs::create_dir_all(&driver_dir_2).expect("failed to create driver-b directory"); - fs::create_dir_all(&ignored_dir_path).expect("failed to create docs directory"); - - let target_arch = CpuArchitecture::Amd64; - let target_dir_1 = expected_target_dir(&driver_dir_1, Some(target_arch), None); - let target_dir_2 = expected_target_dir(&driver_dir_2, Some(target_arch), None); - let target_triple = to_target_triple(target_arch); - let mut test_build_action = TestBuildAction::new( - emulated_workspace.root.clone(), - None, - Some(target_arch), - SignMode::Test { - verify_signature: false, - }, - false, - ); - - test_build_action.expect_detect_wdk_build_number(25100); - test_build_action.expect_root_manifest_exists(&emulated_workspace.root, false); - test_build_action.expect_read_dir_entries(&emulated_workspace.root); - test_build_action.expect_dir_cargo_toml_exists(&driver_dir_1, true); - test_build_action.expect_dir_cargo_toml_exists(&driver_dir_1, true); - test_build_action.expect_dir_cargo_toml_exists(&driver_dir_2, true); - test_build_action.expect_dir_cargo_toml_exists(&ignored_dir_path, false); - test_build_action.expect_dir_cargo_toml_exists(&ignored_dir_path, false); - test_build_action.expect_metadata_for_paths(vec![ - ( - driver_dir_1.clone(), - metadata_from_packages( - &driver_dir_1, - vec![( + #[test] + fn given_a_workspace_member_directory_when_building_then_only_that_member_is_orchestrated() { + let workspace_root = PathBuf::from(r"C:\tmp\workspace"); + let driver_name_1 = "sample-kmdf-1"; + let driver_name_2 = "sample-kmdf-2"; + let driver_dir_1 = workspace_root.join(driver_name_1); + let driver_dir_2 = workspace_root.join(driver_name_2); + let target_arch = CpuArchitecture::Amd64; + let target_dir_2 = expected_target_dir(&driver_dir_2, Some(target_arch), None); + let target_triple = to_target_triple(target_arch); + let mut test_build_action = TestBuildAction::new( + driver_dir_2.clone(), + None, + Some(target_arch), + SignMode::Test { + verify_signature: false, + }, + false, + ) + .set_up_workspace_with_multiple_driver_projects( + &workspace_root, + vec![ + ( driver_name_1, - driver_dir_1.clone(), + driver_dir_1, Some(default_wdk_metadata()), "cdylib", "cdylib", "main.rs", - )], - ), - ), - ( - driver_dir_2.clone(), - metadata_from_packages( - &driver_dir_2, - vec![( + ), + ( driver_name_2, driver_dir_2.clone(), Some(default_wdk_metadata()), "cdylib", "cdylib", "main.rs", - )], - ), - ), - ]); - test_build_action.expect_build_runner( - driver_name_1, - &driver_dir_1, - None, - Some(target_arch), - Ok(cargo_build_messages( - driver_name_1, - DEFAULT_DRIVER_VERSION, - &driver_dir_1, - Some(target_triple.as_str()), + ), + ], + ); + + test_build_action.expect_build_runner( + driver_name_2, + &driver_dir_2, None, - )), - ); - test_build_action.expect_build_runner( - driver_name_2, - &driver_dir_2, - None, - Some(target_arch), - Ok(cargo_build_messages( + Some(target_arch), + Ok(cargo_build_messages( + driver_name_2, + DEFAULT_DRIVER_VERSION, + &driver_dir_2, + Some(target_triple.as_str()), + None, + )), + ); + test_build_action.expect_package_runner( driver_name_2, - DEFAULT_DRIVER_VERSION, &driver_dir_2, - Some(target_triple.as_str()), + &target_dir_2, + target_arch, + SignMode::Test { + verify_signature: false, + }, + false, + ); + + let build_action = initialize_build_action(&mut test_build_action); + let result = build_action.run_from_workspace_root(&driver_dir_2); + + assert!( + result.is_ok(), + "build action failed unexpectedly: {result:?}" + ); + } + + #[test] + fn given_a_workspace_member_when_build_runner_fails_then_the_build_error_is_propagated() { + let workspace_root = PathBuf::from(r"C:\tmp\workspace"); + let cwd = workspace_root.join(DEFAULT_DRIVER_NAME); + let mut test_build_action = TestBuildAction::new( + cwd.clone(), None, - )), - ); - test_build_action.expect_package_runner( - driver_name_1, - &driver_dir_1, - &target_dir_1, - target_arch, - SignMode::Test { - verify_signature: false, - }, - false, - ); - test_build_action.expect_package_runner( - driver_name_2, - &driver_dir_2, - &target_dir_2, - target_arch, - SignMode::Test { - verify_signature: false, - }, - false, - ); + None, + SignMode::Test { + verify_signature: false, + }, + false, + ) + .set_up_workspace_with_multiple_driver_projects( + &workspace_root, + vec![( + DEFAULT_DRIVER_NAME, + cwd.clone(), + Some(default_wdk_metadata()), + "cdylib", + "cdylib", + "main.rs", + )], + ); - let build_action = initialize_build_action(&mut test_build_action); - let result = crate::test_utils::with_env::<&str, &str, _, _>(&[], || build_action.run()); + test_build_action.expect_build_runner( + DEFAULT_DRIVER_NAME, + &cwd, + None, + None, + Err(build_task_error()), + ); - assert!( - result.is_ok(), - "build action failed unexpectedly: {result:?}" - ); + let build_action = initialize_build_action(&mut test_build_action); + let result = build_action.run_from_workspace_root(&cwd); + + assert!(matches!(result, Err(BuildActionError::BuildTask(_)))); + } } -#[test] -fn given_a_workspace_member_when_build_runner_fails_then_the_build_error_is_propagated() { - let workspace_root = PathBuf::from(r"C:\tmp\workspace"); - let cwd = workspace_root.join(DEFAULT_DRIVER_NAME); - let mut test_build_action = TestBuildAction::new( - cwd.clone(), - None, - None, - SignMode::Test { - verify_signature: false, - }, - false, - ) - .set_up_workspace_with_multiple_driver_projects( - &workspace_root, - vec![( - DEFAULT_DRIVER_NAME, - cwd.clone(), - Some(default_wdk_metadata()), - "cdylib", - "cdylib", - "main.rs", - )], - ); +mod emulated_workspace { + use super::*; - test_build_action.expect_build_runner( - DEFAULT_DRIVER_NAME, - &cwd, - None, - None, - Err(build_task_error()), - ); + #[test] + fn given_an_emulated_workspace_when_running_then_each_valid_project_is_built() { + let emulated_workspace = TestWorkspaceRoot::new("build-action-emulated-workspace"); + let driver_name_1 = "driver-a"; + let driver_name_2 = "driver-b"; + let ignored_dir = "docs"; + let driver_dir_1 = emulated_workspace.root.join(driver_name_1); + let driver_dir_2 = emulated_workspace.root.join(driver_name_2); + let ignored_dir_path = emulated_workspace.root.join(ignored_dir); + fs::create_dir_all(&driver_dir_1).expect("failed to create driver-a directory"); + fs::create_dir_all(&driver_dir_2).expect("failed to create driver-b directory"); + fs::create_dir_all(&ignored_dir_path).expect("failed to create docs directory"); + + let target_arch = CpuArchitecture::Amd64; + let target_dir_1 = expected_target_dir(&driver_dir_1, Some(target_arch), None); + let target_dir_2 = expected_target_dir(&driver_dir_2, Some(target_arch), None); + let target_triple = to_target_triple(target_arch); + let mut test_build_action = TestBuildAction::new( + emulated_workspace.root.clone(), + None, + Some(target_arch), + SignMode::Test { + verify_signature: false, + }, + false, + ); + + test_build_action.expect_detect_wdk_build_number(25100); + test_build_action.expect_root_manifest_exists(&emulated_workspace.root, false); + test_build_action.expect_read_dir_entries(&emulated_workspace.root); + test_build_action.expect_dir_cargo_toml_exists(&driver_dir_1, true); + test_build_action.expect_dir_cargo_toml_exists(&driver_dir_1, true); + test_build_action.expect_dir_cargo_toml_exists(&driver_dir_2, true); + test_build_action.expect_dir_cargo_toml_exists(&ignored_dir_path, false); + test_build_action.expect_dir_cargo_toml_exists(&ignored_dir_path, false); + test_build_action.expect_metadata_for_paths(vec![ + ( + driver_dir_1.clone(), + metadata_from_packages( + &driver_dir_1, + vec![( + driver_name_1, + driver_dir_1.clone(), + Some(default_wdk_metadata()), + "cdylib", + "cdylib", + "main.rs", + )], + ), + ), + ( + driver_dir_2.clone(), + metadata_from_packages( + &driver_dir_2, + vec![( + driver_name_2, + driver_dir_2.clone(), + Some(default_wdk_metadata()), + "cdylib", + "cdylib", + "main.rs", + )], + ), + ), + ]); + test_build_action.expect_build_runner( + driver_name_1, + &driver_dir_1, + None, + Some(target_arch), + Ok(cargo_build_messages( + driver_name_1, + DEFAULT_DRIVER_VERSION, + &driver_dir_1, + Some(target_triple.as_str()), + None, + )), + ); + test_build_action.expect_build_runner( + driver_name_2, + &driver_dir_2, + None, + Some(target_arch), + Ok(cargo_build_messages( + driver_name_2, + DEFAULT_DRIVER_VERSION, + &driver_dir_2, + Some(target_triple.as_str()), + None, + )), + ); + test_build_action.expect_package_runner( + driver_name_1, + &driver_dir_1, + &target_dir_1, + target_arch, + SignMode::Test { + verify_signature: false, + }, + false, + ); + test_build_action.expect_package_runner( + driver_name_2, + &driver_dir_2, + &target_dir_2, + target_arch, + SignMode::Test { + verify_signature: false, + }, + false, + ); - let build_action = initialize_build_action(&mut test_build_action); - let result = build_action.run_from_workspace_root(&cwd); + let build_action = initialize_build_action(&mut test_build_action); + let result = crate::test_utils::with_env::<&str, &str, _, _>(&[], || build_action.run()); - assert!(matches!(result, Err(BuildActionError::BuildTask(_)))); + assert!( + result.is_ok(), + "build action failed unexpectedly: {result:?}" + ); + } } fn initialize_build_action(test_build_action: &mut TestBuildAction) -> BuildAction<'_> { From 820c97073d82c71fff1b848748ec22d1220b03da Mon Sep 17 00:00:00 2001 From: krishnakumar4a4 Date: Wed, 15 Jul 2026 16:40:35 +0530 Subject: [PATCH 04/12] refactor: rename harness and simplify test names in build action tests - Rename TestBuildAction to BuildActionHarness for consistency with PackageTaskHarness naming convention in the task layer. - Shorten test function names since submodule names already provide the 'given' context (e.g. standalone_driver_project::run_skips_probe...). - Rename test_build_action parameter to harness throughout. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: fa978dd1-9d61-4cea-a03a-7ebc914228fa --- crates/cargo-wdk/src/actions/build/tests.rs | 198 ++++++++++---------- 1 file changed, 96 insertions(+), 102 deletions(-) diff --git a/crates/cargo-wdk/src/actions/build/tests.rs b/crates/cargo-wdk/src/actions/build/tests.rs index 8585aa4e2..4dbef262b 100644 --- a/crates/cargo-wdk/src/actions/build/tests.rs +++ b/crates/cargo-wdk/src/actions/build/tests.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation // License: MIT OR Apache-2.0 -#![allow(clippy::too_many_lines)] -#![allow(clippy::ref_option_ref)] +#![allow(clippy::too_many_lines)] // Package tests are longer and splitting them into sub functions can make the code less readable +#![allow(clippy::ref_option_ref)] // This is suppressed for mockall as it generates mocks with env_vars: &Option use std::{ collections::HashMap, @@ -53,11 +53,10 @@ mod standalone_driver_project { use super::*; #[test] - fn given_a_driver_project_when_target_arch_is_detected_then_build_action_orchestrates_build_and_package() - { + fn run_orchestrates_build_and_package_when_target_arch_is_detected() { let cwd = PathBuf::from(r"C:\tmp\sample-driver"); let target_dir = expected_target_dir(&cwd, None, None); - let mut test_build_action = TestBuildAction::new( + let mut harness = BuildActionHarness::new( cwd.clone(), None, None, @@ -68,7 +67,7 @@ mod standalone_driver_project { ) .set_up_standalone_driver_project(DEFAULT_DRIVER_NAME, Some(default_wdk_metadata())); - test_build_action.expect_build_runner( + harness.expect_build_runner( DEFAULT_DRIVER_NAME, &cwd, None, @@ -81,8 +80,8 @@ mod standalone_driver_project { None, )), ); - test_build_action.expect_probe_target_arch_using_cargo_rustc(&cwd, CpuArchitecture::Amd64); - test_build_action.expect_package_runner( + harness.expect_probe_target_arch_using_cargo_rustc(&cwd, CpuArchitecture::Amd64); + harness.expect_package_runner( DEFAULT_DRIVER_NAME, &cwd, &target_dir, @@ -93,7 +92,7 @@ mod standalone_driver_project { false, ); - let build_action = initialize_build_action(&mut test_build_action); + let build_action = initialize_build_action(&mut harness); let result = build_action.run_from_workspace_root(&cwd); assert!( @@ -103,13 +102,13 @@ mod standalone_driver_project { } #[test] - fn given_explicit_target_arch_when_building_then_probe_is_skipped_and_package_runner_uses_it() { + fn run_skips_probe_and_uses_explicit_target_arch() { let cwd = PathBuf::from(r"C:\tmp\sample-driver"); let profile = Some(Profile::Release); let target_arch = CpuArchitecture::Arm64; let target_dir = expected_target_dir(&cwd, Some(target_arch), profile); let target_triple = to_target_triple(target_arch); - let mut test_build_action = TestBuildAction::new( + let mut harness = BuildActionHarness::new( cwd.clone(), profile, Some(target_arch), @@ -120,7 +119,7 @@ mod standalone_driver_project { ) .set_up_standalone_driver_project(DEFAULT_DRIVER_NAME, Some(default_wdk_metadata())); - test_build_action.expect_build_runner( + harness.expect_build_runner( DEFAULT_DRIVER_NAME, &cwd, profile, @@ -133,7 +132,7 @@ mod standalone_driver_project { profile, )), ); - test_build_action.expect_package_runner( + harness.expect_package_runner( DEFAULT_DRIVER_NAME, &cwd, &target_dir, @@ -144,7 +143,7 @@ mod standalone_driver_project { true, ); - let build_action = initialize_build_action(&mut test_build_action); + let build_action = initialize_build_action(&mut harness); let result = build_action.run_from_workspace_root(&cwd); assert!( @@ -154,9 +153,9 @@ mod standalone_driver_project { } #[test] - fn given_a_non_driver_package_when_building_then_package_runner_is_skipped() { + fn run_skips_package_runner_for_non_driver_package() { let cwd = PathBuf::from(r"C:\tmp\non-driver"); - let mut test_build_action = TestBuildAction::new( + let mut harness = BuildActionHarness::new( cwd.clone(), None, None, @@ -167,7 +166,7 @@ mod standalone_driver_project { ) .set_up_standalone_driver_project(DEFAULT_DRIVER_NAME, None); - test_build_action.expect_build_runner( + harness.expect_build_runner( DEFAULT_DRIVER_NAME, &cwd, None, @@ -180,12 +179,9 @@ mod standalone_driver_project { None, )), ); - test_build_action - .mock_package_task_runner - .expect_run() - .never(); + harness.mock_package_task_runner.expect_run().never(); - let build_action = initialize_build_action(&mut test_build_action); + let build_action = initialize_build_action(&mut harness); let result = build_action.run_from_workspace_root(&cwd); assert!( @@ -195,9 +191,9 @@ mod standalone_driver_project { } #[test] - fn given_a_driver_package_without_cdylib_when_building_then_package_runner_is_skipped() { + fn run_skips_package_runner_when_no_cdylib_target() { let cwd = PathBuf::from(r"C:\tmp\driver-lib"); - let mut test_build_action = TestBuildAction::new( + let mut harness = BuildActionHarness::new( cwd.clone(), None, None, @@ -233,19 +229,10 @@ mod standalone_driver_project { None, )); - test_build_action.expect_build_runner( - DEFAULT_DRIVER_NAME, - &cwd, - None, - None, - Ok(Vec::new()), - ); - test_build_action - .mock_package_task_runner - .expect_run() - .never(); + harness.expect_build_runner(DEFAULT_DRIVER_NAME, &cwd, None, None, Ok(Vec::new())); + harness.mock_package_task_runner.expect_run().never(); - let build_action = initialize_build_action(&mut test_build_action); + let build_action = initialize_build_action(&mut harness); let result = build_action.run_from_workspace_root(&cwd); assert!( @@ -259,7 +246,7 @@ mod driver_workspace { use super::*; #[test] - fn given_a_workspace_root_when_one_member_fails_then_workspace_error_is_returned() { + fn run_returns_workspace_error_when_one_member_fails() { let workspace_root = PathBuf::from(r"C:\tmp\workspace"); let driver_name_1 = "sample-kmdf-1"; let driver_name_2 = "sample-kmdf-2"; @@ -268,7 +255,7 @@ mod driver_workspace { let target_arch = CpuArchitecture::Amd64; let target_dir_1 = expected_target_dir(&driver_dir_1, Some(target_arch), None); let target_triple = to_target_triple(target_arch); - let mut test_build_action = TestBuildAction::new( + let mut harness = BuildActionHarness::new( workspace_root.clone(), None, Some(target_arch), @@ -299,7 +286,7 @@ mod driver_workspace { ], ); - test_build_action.expect_build_runner( + harness.expect_build_runner( driver_name_1, &driver_dir_1, None, @@ -312,14 +299,14 @@ mod driver_workspace { None, )), ); - test_build_action.expect_build_runner( + harness.expect_build_runner( driver_name_2, &driver_dir_2, None, Some(target_arch), Err(build_task_error()), ); - test_build_action.expect_package_runner( + harness.expect_package_runner( driver_name_1, &driver_dir_1, &target_dir_1, @@ -330,7 +317,7 @@ mod driver_workspace { false, ); - let build_action = initialize_build_action(&mut test_build_action); + let build_action = initialize_build_action(&mut harness); let result = build_action.run_from_workspace_root(&workspace_root); assert!(matches!( @@ -341,7 +328,7 @@ mod driver_workspace { } #[test] - fn given_a_workspace_member_directory_when_building_then_only_that_member_is_orchestrated() { + fn run_orchestrates_only_targeted_member() { let workspace_root = PathBuf::from(r"C:\tmp\workspace"); let driver_name_1 = "sample-kmdf-1"; let driver_name_2 = "sample-kmdf-2"; @@ -350,7 +337,7 @@ mod driver_workspace { let target_arch = CpuArchitecture::Amd64; let target_dir_2 = expected_target_dir(&driver_dir_2, Some(target_arch), None); let target_triple = to_target_triple(target_arch); - let mut test_build_action = TestBuildAction::new( + let mut harness = BuildActionHarness::new( driver_dir_2.clone(), None, Some(target_arch), @@ -381,7 +368,7 @@ mod driver_workspace { ], ); - test_build_action.expect_build_runner( + harness.expect_build_runner( driver_name_2, &driver_dir_2, None, @@ -394,7 +381,7 @@ mod driver_workspace { None, )), ); - test_build_action.expect_package_runner( + harness.expect_package_runner( driver_name_2, &driver_dir_2, &target_dir_2, @@ -405,7 +392,7 @@ mod driver_workspace { false, ); - let build_action = initialize_build_action(&mut test_build_action); + let build_action = initialize_build_action(&mut harness); let result = build_action.run_from_workspace_root(&driver_dir_2); assert!( @@ -415,10 +402,10 @@ mod driver_workspace { } #[test] - fn given_a_workspace_member_when_build_runner_fails_then_the_build_error_is_propagated() { + fn run_propagates_build_error_on_failure() { let workspace_root = PathBuf::from(r"C:\tmp\workspace"); let cwd = workspace_root.join(DEFAULT_DRIVER_NAME); - let mut test_build_action = TestBuildAction::new( + let mut harness = BuildActionHarness::new( cwd.clone(), None, None, @@ -439,7 +426,7 @@ mod driver_workspace { )], ); - test_build_action.expect_build_runner( + harness.expect_build_runner( DEFAULT_DRIVER_NAME, &cwd, None, @@ -447,7 +434,7 @@ mod driver_workspace { Err(build_task_error()), ); - let build_action = initialize_build_action(&mut test_build_action); + let build_action = initialize_build_action(&mut harness); let result = build_action.run_from_workspace_root(&cwd); assert!(matches!(result, Err(BuildActionError::BuildTask(_)))); @@ -458,7 +445,7 @@ mod emulated_workspace { use super::*; #[test] - fn given_an_emulated_workspace_when_running_then_each_valid_project_is_built() { + fn run_builds_each_valid_project() { let emulated_workspace = TestWorkspaceRoot::new("build-action-emulated-workspace"); let driver_name_1 = "driver-a"; let driver_name_2 = "driver-b"; @@ -474,7 +461,7 @@ mod emulated_workspace { let target_dir_1 = expected_target_dir(&driver_dir_1, Some(target_arch), None); let target_dir_2 = expected_target_dir(&driver_dir_2, Some(target_arch), None); let target_triple = to_target_triple(target_arch); - let mut test_build_action = TestBuildAction::new( + let mut harness = BuildActionHarness::new( emulated_workspace.root.clone(), None, Some(target_arch), @@ -484,15 +471,15 @@ mod emulated_workspace { false, ); - test_build_action.expect_detect_wdk_build_number(25100); - test_build_action.expect_root_manifest_exists(&emulated_workspace.root, false); - test_build_action.expect_read_dir_entries(&emulated_workspace.root); - test_build_action.expect_dir_cargo_toml_exists(&driver_dir_1, true); - test_build_action.expect_dir_cargo_toml_exists(&driver_dir_1, true); - test_build_action.expect_dir_cargo_toml_exists(&driver_dir_2, true); - test_build_action.expect_dir_cargo_toml_exists(&ignored_dir_path, false); - test_build_action.expect_dir_cargo_toml_exists(&ignored_dir_path, false); - test_build_action.expect_metadata_for_paths(vec![ + harness.expect_detect_wdk_build_number(25100); + harness.expect_root_manifest_exists(&emulated_workspace.root, false); + harness.expect_read_dir_entries(&emulated_workspace.root); + harness.expect_dir_cargo_toml_exists(&driver_dir_1, true); + harness.expect_dir_cargo_toml_exists(&driver_dir_1, true); + harness.expect_dir_cargo_toml_exists(&driver_dir_2, true); + harness.expect_dir_cargo_toml_exists(&ignored_dir_path, false); + harness.expect_dir_cargo_toml_exists(&ignored_dir_path, false); + harness.expect_metadata_for_paths(vec![ ( driver_dir_1.clone(), metadata_from_packages( @@ -522,7 +509,7 @@ mod emulated_workspace { ), ), ]); - test_build_action.expect_build_runner( + harness.expect_build_runner( driver_name_1, &driver_dir_1, None, @@ -535,7 +522,7 @@ mod emulated_workspace { None, )), ); - test_build_action.expect_build_runner( + harness.expect_build_runner( driver_name_2, &driver_dir_2, None, @@ -548,7 +535,7 @@ mod emulated_workspace { None, )), ); - test_build_action.expect_package_runner( + harness.expect_package_runner( driver_name_1, &driver_dir_1, &target_dir_1, @@ -558,7 +545,7 @@ mod emulated_workspace { }, false, ); - test_build_action.expect_package_runner( + harness.expect_package_runner( driver_name_2, &driver_dir_2, &target_dir_2, @@ -569,7 +556,7 @@ mod emulated_workspace { false, ); - let build_action = initialize_build_action(&mut test_build_action); + let build_action = initialize_build_action(&mut harness); let result = crate::test_utils::with_env::<&str, &str, _, _>(&[], || build_action.run()); assert!( @@ -579,24 +566,24 @@ mod emulated_workspace { } } -fn initialize_build_action(test_build_action: &mut TestBuildAction) -> BuildAction<'_> { +fn initialize_build_action(harness: &mut BuildActionHarness) -> BuildAction<'_> { BuildAction::new_with_runners( &BuildActionParams { - working_dir: &test_build_action.cwd, - profile: test_build_action.profile.as_ref(), - target_arch: test_build_action.target_arch, - sign_mode: test_build_action.sign_mode, - is_sample_class: test_build_action.sample_class, - locked: test_build_action.locked, - features: &test_build_action.features, + working_dir: &harness.cwd, + profile: harness.profile.as_ref(), + target_arch: harness.target_arch, + sign_mode: harness.sign_mode, + is_sample_class: harness.sample_class, + locked: harness.locked, + features: &harness.features, verbosity_level: clap_verbosity_flag::Verbosity::new(1, 0), }, - &test_build_action.mock_wdk_build_provider, - &test_build_action.mock_run_command, - &test_build_action.mock_fs_provider, - &test_build_action.mock_metadata_provider, - std::mem::take(&mut test_build_action.mock_build_task_runner), - std::mem::take(&mut test_build_action.mock_package_task_runner), + &harness.mock_wdk_build_provider, + &harness.mock_run_command, + &harness.mock_fs_provider, + &harness.mock_metadata_provider, + std::mem::take(&mut harness.mock_build_task_runner), + std::mem::take(&mut harness.mock_package_task_runner), ) .expect("failed to initialize build action") } @@ -644,7 +631,7 @@ fn build_task_error() -> super::error::BuildTaskError { )) } -struct TestBuildAction { +struct BuildActionHarness { cwd: PathBuf, profile: Option, target_arch: Option, @@ -660,7 +647,7 @@ struct TestBuildAction { mock_package_task_runner: MockPackageTaskRunner, } -impl TestBuildAction { +impl BuildActionHarness { fn new( cwd: PathBuf, profile: Option, @@ -1000,6 +987,7 @@ fn get_cargo_metadata( .map(|p| p.0) .collect::>() .join(", "), + // Require quotes around each member workspace_member_list .iter() .map(|s| format!("\"{}\"", s.0)) @@ -1096,6 +1084,9 @@ fn get_cargo_metadata_wdk_metadata( )) } +/// Creates a valid cargo compiler-artifact JSON message for testing. +/// This simulates the JSON output that `cargo build --message-format=json` +/// produces. fn create_cargo_build_output_json( package_name: &str, package_version: &str, @@ -1120,6 +1111,7 @@ fn strip_windows_extended_prefix(path: &Path) -> String { return path_str.into_owned(); }; + // Handle UNC paths without_prefix.strip_prefix("UNC\\").map_or_else( || without_prefix.to_string(), |unc_rest| format!(r"\\{unc_rest}"), @@ -1136,16 +1128,20 @@ fn create_cargo_build_output_json_with_manifest( is_driver: bool, ) -> Output { let normalized_name = package_name.replace('-', "_"); + // Determine profile directory name let profile_dir = match profile { Some(Profile::Release) => "release", _ => "debug", }; + // For non-driver projects, use "lib" instead of "cdylib" to ensure BuildTask + // returns DllNotFound let (kind, crate_types, file_ext) = if is_driver { ("cdylib", "cdylib", "dll") } else { ("lib", "lib", "rlib") }; + // Build the artifact path based on target_triple and profile let mut artifact_path = workspace_root.join("target"); if let Some(target) = target_triple { artifact_path = artifact_path.join(target); @@ -1391,11 +1387,11 @@ mod get_target_arch_from_cargo_rustc { use wdk_build::CpuArchitecture; - use super::{BuildActionError, SignMode, TestBuildAction, initialize_build_action}; + use super::{BuildActionError, BuildActionHarness, SignMode, initialize_build_action}; fn run_parse_test(cfg_output: Vec, expected_arch: CpuArchitecture) { let cwd = PathBuf::from(r"C:\tmp"); - let mut test_build_action = TestBuildAction::new( + let mut harness = BuildActionHarness::new( cwd.clone(), None, None, @@ -1404,9 +1400,9 @@ mod get_target_arch_from_cargo_rustc { }, false, ); - expect_cargo_rustc_print_cfg(&mut test_build_action, cwd.clone(), cfg_output); + expect_cargo_rustc_print_cfg(&mut harness, cwd.clone(), cfg_output); - let build_action = initialize_build_action(&mut test_build_action); + let build_action = initialize_build_action(&mut harness); let arch = build_action .get_target_arch_from_cargo_rustc(&cwd) .expect("Expected target arch to be detected"); @@ -1437,7 +1433,7 @@ mod get_target_arch_from_cargo_rustc { #[test] fn unsupported_arch_returns_error() { let cwd = PathBuf::from(r"C:\tmp"); - let mut test_build_action = TestBuildAction::new( + let mut harness = BuildActionHarness::new( cwd.clone(), None, None, @@ -1447,12 +1443,12 @@ mod get_target_arch_from_cargo_rustc { false, ); expect_cargo_rustc_print_cfg( - &mut test_build_action, + &mut harness, cwd.clone(), b"target_arch=\"mips\"\n".to_vec(), ); - let build_action = initialize_build_action(&mut test_build_action); + let build_action = initialize_build_action(&mut harness); let err = build_action .get_target_arch_from_cargo_rustc(&cwd) .expect_err("Expected UnsupportedArchitecture error"); @@ -1462,7 +1458,7 @@ mod get_target_arch_from_cargo_rustc { #[test] fn missing_target_arch_returns_error() { let cwd = PathBuf::from(r"C:\tmp"); - let mut test_build_action = TestBuildAction::new( + let mut harness = BuildActionHarness::new( cwd.clone(), None, None, @@ -1472,12 +1468,12 @@ mod get_target_arch_from_cargo_rustc { false, ); expect_cargo_rustc_print_cfg( - &mut test_build_action, + &mut harness, cwd.clone(), b"some_other_cfg=\"value\"\n".to_vec(), ); - let build_action = initialize_build_action(&mut test_build_action); + let build_action = initialize_build_action(&mut harness); let err = build_action .get_target_arch_from_cargo_rustc(&cwd) .expect_err("Expected CannotDetectTargetArch error"); @@ -1487,7 +1483,7 @@ mod get_target_arch_from_cargo_rustc { #[test] fn invalid_utf8_returns_error() { let cwd = PathBuf::from(r"C:\tmp"); - let mut test_build_action = TestBuildAction::new( + let mut harness = BuildActionHarness::new( cwd.clone(), None, None, @@ -1496,9 +1492,9 @@ mod get_target_arch_from_cargo_rustc { }, false, ); - expect_cargo_rustc_print_cfg(&mut test_build_action, cwd.clone(), vec![0xFF, 0xFE]); + expect_cargo_rustc_print_cfg(&mut harness, cwd.clone(), vec![0xFF, 0xFE]); - let build_action = initialize_build_action(&mut test_build_action); + let build_action = initialize_build_action(&mut harness); let err = build_action .get_target_arch_from_cargo_rustc(&cwd) .expect_err("Expected CannotDetectTargetArch error"); @@ -1506,19 +1502,17 @@ mod get_target_arch_from_cargo_rustc { } fn expect_cargo_rustc_print_cfg( - test_build_action: &mut TestBuildAction, + harness: &mut BuildActionHarness, cwd: PathBuf, stdout: Vec, ) { let mut expected_args: Vec = vec!["rustc".to_string()]; - if test_build_action.locked { + if harness.locked { expected_args.push("--locked".to_string()); } - expected_args.extend(super::super::features_to_cargo_args( - &test_build_action.features, - )); + expected_args.extend(super::super::features_to_cargo_args(&harness.features)); expected_args.extend(["--".to_string(), "--print".to_string(), "cfg".to_string()]); - test_build_action + harness .mock_run_command .expect_run() .withf( From 14ee21882a80dc7024858e85cf3cbb3321ee25d5 Mon Sep 17 00:00:00 2001 From: krishnakumar4a4 Date: Wed, 15 Jul 2026 18:44:03 +0530 Subject: [PATCH 05/12] refactor: simplify PackageTaskParams usage in run function --- .../src/actions/build/package_task.rs | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/crates/cargo-wdk/src/actions/build/package_task.rs b/crates/cargo-wdk/src/actions/build/package_task.rs index eacdffb35..4824ec92f 100644 --- a/crates/cargo-wdk/src/actions/build/package_task.rs +++ b/crates/cargo-wdk/src/actions/build/package_task.rs @@ -51,7 +51,7 @@ const WDR_TEST_CERT_STORE: &str = "WDRTestCertStore"; const WDR_LOCAL_TEST_CERT: &str = "WDRLocalTestCert"; const STAMPINF_VERSION_ENV_VAR: &str = "STAMPINF_VERSION"; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct PackageTaskParams<'a> { pub package_name: &'a str, pub working_dir: &'a Path, @@ -76,21 +76,7 @@ impl PackageTaskRunner { command_exec: &CommandExec, fs: &Fs, ) -> Result<(), PackageTaskError> { - PackageTask::new( - PackageTaskParams { - package_name: params.package_name, - working_dir: params.working_dir, - target_dir: params.target_dir, - target_arch: params.target_arch, - sign_mode: params.sign_mode, - sample_class: params.sample_class, - driver_model: params.driver_model.clone(), - }, - wdk_build, - command_exec, - fs, - ) - .run() + PackageTask::new(params.clone(), wdk_build, command_exec, fs).run() } } From c22da52b2bd1fc294cc8117fd317bc20a629665c Mon Sep 17 00:00:00 2001 From: krishnakumar4a4 Date: Wed, 15 Jul 2026 19:09:55 +0530 Subject: [PATCH 06/12] refactor: return lazy boxed iterator from BuildTaskRunner BuildTaskRunner::run previously collected the entire cargo message stream into a Vec, which forced every message to be parsed and allocated up front and prevented get_target_dir_from_output from short-circuiting once the matching cdylib artifact is found. Return Box> instead so the runner forwards BuildTask::run's lazy stream while still being mockable (mockall cannot mock an opaque impl Trait return, but can mock a boxed trait object). BuildTask::run gains + use<> precise capturing so its owned, 'static iterator can move out of the temporary BuildTask and be boxed. Addresses PR review comment r3581534373. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: fa978dd1-9d61-4cea-a03a-7ebc914228fa --- .../cargo-wdk/src/actions/build/build_task.rs | 22 ++++++++++++++----- crates/cargo-wdk/src/actions/build/mod.rs | 3 +-- crates/cargo-wdk/src/actions/build/tests.rs | 7 +++++- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/crates/cargo-wdk/src/actions/build/build_task.rs b/crates/cargo-wdk/src/actions/build/build_task.rs index 543b3d5e3..a0f82ff73 100644 --- a/crates/cargo-wdk/src/actions/build/build_task.rs +++ b/crates/cargo-wdk/src/actions/build/build_task.rs @@ -50,14 +50,21 @@ pub struct BuildTaskRunner {} #[automock] #[allow(dead_code, clippy::unused_self, clippy::elidable_lifetime_names)] impl BuildTaskRunner { + // Returns `Box>` rather than `impl Iterator<...>` because + // this method is `#[automock]`ed. mockall must be able to *name* the return + // type to generate the mock's expectation storage, and it cannot mock an + // opaque `impl Trait` return. Boxing into a trait object gives mockall a + // concrete, nameable type while still forwarding `BuildTask::run`'s lazy + // message stream, so consumers like `get_target_dir_from_output` can + // short-circuit instead of parsing/allocating every cargo message up front. pub fn run<'a>( &self, params: &BuildTaskParams<'a>, command_exec: &CommandExec, - ) -> Result>, BuildTaskError> { - BuildTask::new(*params, command_exec) - .run() - .map(Iterator::collect) + ) -> Result>>, BuildTaskError> { + BuildTask::new(*params, command_exec).run().map(|messages| { + Box::new(messages) as Box>> + }) } } @@ -110,9 +117,14 @@ impl<'a> BuildTask<'a> { /// not a valid unicode /// * `BuildTaskError::CargoBuild` - If there is an error running the `cargo /// build` command + // `+ use<>` opts this RPIT out of capturing the `&self` lifetime (edition + // 2024 captures in-scope lifetimes by default). The returned iterator owns + // its buffer (`Cursor>`), so it is effectively `'static`; opting out + // of the capture lets `BuildTaskRunner::run` move it out of the temporary + // `BuildTask` and box it as a `'static` trait object. pub fn run( &self, - ) -> Result>, BuildTaskError> { + ) -> Result> + use<>, BuildTaskError> { debug!("Running cargo build"); let mut args = vec!["build".to_string()]; args.push("--message-format=json-render-diagnostics".to_string()); diff --git a/crates/cargo-wdk/src/actions/build/mod.rs b/crates/cargo-wdk/src/actions/build/mod.rs index 538f8208a..fd1bbfa37 100644 --- a/crates/cargo-wdk/src/actions/build/mod.rs +++ b/crates/cargo-wdk/src/actions/build/mod.rs @@ -418,8 +418,7 @@ impl<'a> BuildAction<'a> { self.get_target_arch_from_cargo_rustc(working_dir)? }; debug!("Target architecture for package: {package_name} is: {target_arch}"); - let target_dir = - Self::get_target_dir_from_output(package, output_message_iter.into_iter())?; + let target_dir = Self::get_target_dir_from_output(package, output_message_iter)?; debug!( "Target directory for package: {} is: {}", package_name, diff --git a/crates/cargo-wdk/src/actions/build/tests.rs b/crates/cargo-wdk/src/actions/build/tests.rs index 4dbef262b..1bde916f4 100644 --- a/crates/cargo-wdk/src/actions/build/tests.rs +++ b/crates/cargo-wdk/src/actions/build/tests.rs @@ -822,7 +822,12 @@ impl BuildActionHarness { && params.features.features == expected_features.features }) .once() - .return_once(move |_, _| result); + .return_once(move |_, _| { + result.map(|messages| { + Box::new(messages.into_iter()) + as Box>> + }) + }); } fn expect_package_runner( From 9d266846175ab394a2be136f260b29a43333f1d8 Mon Sep 17 00:00:00 2001 From: krishnakumar4a4 Date: Wed, 15 Jul 2026 20:43:58 +0530 Subject: [PATCH 07/12] refactor: update dead code attributes for BuildTaskRunner and PackageTaskRunner --- crates/cargo-wdk/src/actions/build/build_task.rs | 5 +++-- crates/cargo-wdk/src/actions/build/package_task.rs | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/cargo-wdk/src/actions/build/build_task.rs b/crates/cargo-wdk/src/actions/build/build_task.rs index a0f82ff73..4c891b881 100644 --- a/crates/cargo-wdk/src/actions/build/build_task.rs +++ b/crates/cargo-wdk/src/actions/build/build_task.rs @@ -44,11 +44,12 @@ pub struct BuildTaskParams<'a> { } #[derive(Debug, Default)] -#[allow(dead_code)] +#[cfg_attr(test, allow(dead_code))] pub struct BuildTaskRunner {} #[automock] -#[allow(dead_code, clippy::unused_self, clippy::elidable_lifetime_names)] +#[allow(clippy::unused_self, clippy::elidable_lifetime_names)] +#[cfg_attr(test, allow(dead_code))] impl BuildTaskRunner { // Returns `Box>` rather than `impl Iterator<...>` because // this method is `#[automock]`ed. mockall must be able to *name* the return diff --git a/crates/cargo-wdk/src/actions/build/package_task.rs b/crates/cargo-wdk/src/actions/build/package_task.rs index 4824ec92f..8ec937e6c 100644 --- a/crates/cargo-wdk/src/actions/build/package_task.rs +++ b/crates/cargo-wdk/src/actions/build/package_task.rs @@ -63,11 +63,12 @@ pub struct PackageTaskParams<'a> { } #[derive(Debug, Default)] -#[allow(dead_code)] +#[cfg_attr(test, allow(dead_code))] pub struct PackageTaskRunner {} #[automock] -#[allow(dead_code, clippy::unused_self, clippy::elidable_lifetime_names)] +#[allow(clippy::unused_self, clippy::elidable_lifetime_names)] +#[cfg_attr(test, allow(dead_code))] impl PackageTaskRunner { pub fn run<'a>( &self, From 61a6884510bba526f0180523a2623b08f7af215b Mon Sep 17 00:00:00 2001 From: krishnakumar4a4 Date: Thu, 16 Jul 2026 09:22:54 +0530 Subject: [PATCH 08/12] refactor: improve test mutex handling and enhance feature forwarding in build action tests --- .../src/actions/build/package_task.rs | 41 ++++-- crates/cargo-wdk/src/actions/build/tests.rs | 137 ++++++++++++++++++ 2 files changed, 169 insertions(+), 9 deletions(-) diff --git a/crates/cargo-wdk/src/actions/build/package_task.rs b/crates/cargo-wdk/src/actions/build/package_task.rs index 8ec937e6c..35d10bc2e 100644 --- a/crates/cargo-wdk/src/actions/build/package_task.rs +++ b/crates/cargo-wdk/src/actions/build/package_task.rs @@ -684,6 +684,11 @@ mod tests { // Other tests that call `run_stampinf()` read the same env var, so parallel // execution causes non-deterministic failures when the env var is // unexpectedly set. + // + // Lock acquisition recovers from poisoning + // (`unwrap_or_else(PoisonError::into_inner)`) so that a panic in one test does + // not cascade into unrelated failures across every other test that takes this + // lock. static TEST_MUTEX: Mutex<()> = Mutex::new(()); #[test] @@ -812,7 +817,9 @@ mod tests { #[test] fn run_packages_driver_with_expected_operations() { - let _lock = TEST_MUTEX.lock().unwrap(); + let _lock = TEST_MUTEX + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); @@ -863,7 +870,9 @@ mod tests { #[test] fn run_verifies_signatures_when_enabled() { - let _lock = TEST_MUTEX.lock().unwrap(); + let _lock = TEST_MUTEX + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let mut harness = PackageTaskHarness::new().with_sign_mode(SignMode::Test { verify_signature: true, }); @@ -885,7 +894,9 @@ mod tests { #[test] fn run_exports_certificate_from_store_when_it_already_exists() { - let _lock = TEST_MUTEX.lock().unwrap(); + let _lock = TEST_MUTEX + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); @@ -934,7 +945,9 @@ mod tests { #[test] fn run_returns_error_when_inx_file_is_missing() { - let _lock = TEST_MUTEX.lock().unwrap(); + let _lock = TEST_MUTEX + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); harness.expect_exists(paths.src_inx_file_path.clone(), false); @@ -951,7 +964,9 @@ mod tests { #[test] fn run_returns_error_when_copying_driver_binary_fails() { - let _lock = TEST_MUTEX.lock().unwrap(); + let _lock = TEST_MUTEX + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); @@ -986,7 +1001,9 @@ mod tests { #[test] fn run_returns_error_when_stampinf_fails() { - let _lock = TEST_MUTEX.lock().unwrap(); + let _lock = TEST_MUTEX + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); @@ -1024,7 +1041,9 @@ mod tests { #[test] fn run_returns_error_when_inf2cat_fails() { - let _lock = TEST_MUTEX.lock().unwrap(); + let _lock = TEST_MUTEX + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); @@ -1063,7 +1082,9 @@ mod tests { #[test] fn run_skips_infverif_for_samples_when_wdk_build_is_in_bugged_range() { - let _lock = TEST_MUTEX.lock().unwrap(); + let _lock = TEST_MUTEX + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let mut harness = PackageTaskHarness::new().with_sample_class(true); let paths = harness.paths(); @@ -1113,7 +1134,9 @@ mod tests { #[test] fn stampinf_version_overrides_with_env_var() { - let _lock = TEST_MUTEX.lock().unwrap(); + let _lock = TEST_MUTEX + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); // verify both with and without the env var set scenarios let scenarios = [ ("env_set", Some("1.2.3.4"), true), diff --git a/crates/cargo-wdk/src/actions/build/tests.rs b/crates/cargo-wdk/src/actions/build/tests.rs index 1bde916f4..650d5647e 100644 --- a/crates/cargo-wdk/src/actions/build/tests.rs +++ b/crates/cargo-wdk/src/actions/build/tests.rs @@ -240,6 +240,60 @@ mod standalone_driver_project { "build action failed unexpectedly: {result:?}" ); } + + #[test] + fn run_forwards_locked_and_features_to_metadata() { + let cwd = PathBuf::from(r"C:\tmp\sample-driver"); + let target_dir = expected_target_dir(&cwd, None, None); + let mut harness = BuildActionHarness::new( + cwd.clone(), + None, + None, + SignMode::Test { + verify_signature: false, + }, + false, + ) + .with_locked(true) + .with_features(non_default_features()) + .set_up_standalone_driver_project(DEFAULT_DRIVER_NAME, Some(default_wdk_metadata())); + + harness.expect_build_runner( + DEFAULT_DRIVER_NAME, + &cwd, + None, + None, + Ok(cargo_build_messages( + DEFAULT_DRIVER_NAME, + DEFAULT_DRIVER_VERSION, + &cwd, + None, + None, + )), + ); + harness.expect_probe_target_arch_using_cargo_rustc(&cwd, CpuArchitecture::Amd64); + harness.expect_package_runner( + DEFAULT_DRIVER_NAME, + &cwd, + &target_dir, + CpuArchitecture::Amd64, + SignMode::Test { + verify_signature: false, + }, + false, + ); + + // The metadata, build-runner and cargo-rustc-probe expectations all assert + // that `--locked` and the selected features are forwarded; the run only + // succeeds if `BuildAction` forwards them to every downstream call. + let build_action = initialize_build_action(&mut harness); + let result = build_action.run_from_workspace_root(&cwd); + + assert!( + result.is_ok(), + "build action failed unexpectedly: {result:?}" + ); + } } mod driver_workspace { @@ -592,6 +646,26 @@ fn default_wdk_metadata() -> TestWdkMetadata { get_cargo_metadata_wdk_metadata("KMDF", 1, 33) } +fn features_match(expected: &Features, actual: &Features) -> bool { + expected.all_features == actual.all_features + && expected.no_default_features == actual.no_default_features + && expected.features == actual.features +} + +fn forwards_locked(other_options: &[String], expected_locked: bool) -> bool { + other_options.iter().any(|opt| opt.as_str() == "--locked") == expected_locked +} + +#[allow(clippy::field_reassign_with_default)] +fn non_default_features() -> Features { + // `Features` is `#[non_exhaustive]`, so it can't be built with a struct + // literal; mutate a default instead. + let mut features = Features::default(); + features.no_default_features = true; + features.features = vec!["sample-feature".to_string()]; + features +} + fn expected_target_dir( cwd: &Path, target_arch: Option, @@ -672,6 +746,16 @@ impl BuildActionHarness { } } + fn with_locked(mut self, locked: bool) -> Self { + self.locked = locked; + self + } + + fn with_features(mut self, features: Features) -> Self { + self.features = features; + self + } + fn set_up_standalone_driver_project( mut self, package_name: &str, @@ -690,8 +774,14 @@ impl BuildActionHarness { )], ); let cargo_toml_metadata_for_closure = cargo_toml_metadata; + let expected_locked = self.locked; + let expected_features = self.features.clone(); self.mock_metadata_provider .expect_get_cargo_metadata_at_path() + .withf(move |_working_dir, other_options, features| { + forwards_locked(other_options, expected_locked) + && features_match(&expected_features, features) + }) .once() .returning(move |_, _, _| Ok(cargo_toml_metadata_for_closure.clone())); self @@ -704,8 +794,14 @@ impl BuildActionHarness { ) -> Self { let cargo_toml_metadata = metadata_from_packages(workspace_root_dir, packages); let cargo_toml_metadata_for_closure = cargo_toml_metadata; + let expected_locked = self.locked; + let expected_features = self.features.clone(); self.mock_metadata_provider .expect_get_cargo_metadata_at_path() + .withf(move |_working_dir, other_options, features| { + forwards_locked(other_options, expected_locked) + && features_match(&expected_features, features) + }) .once() .returning(move |_, _, _| Ok(cargo_toml_metadata_for_closure.clone())); self @@ -715,16 +811,28 @@ impl BuildActionHarness { let cargo_toml_metadata = serde_json::from_str::(cargo_toml_metadata) .expect("failed to parse cargo metadata"); let cargo_toml_metadata_for_closure = cargo_toml_metadata; + let expected_locked = self.locked; + let expected_features = self.features.clone(); self.mock_metadata_provider .expect_get_cargo_metadata_at_path() + .withf(move |_working_dir, other_options, features| { + forwards_locked(other_options, expected_locked) + && features_match(&expected_features, features) + }) .once() .returning(move |_, _, _| Ok(cargo_toml_metadata_for_closure.clone())); self } fn expect_metadata_for_paths(&mut self, metadata_by_path: Vec<(PathBuf, CargoMetadata)>) { + let expected_locked = self.locked; + let expected_features = self.features.clone(); self.mock_metadata_provider .expect_get_cargo_metadata_at_path() + .withf(move |_working_dir, other_options, features| { + forwards_locked(other_options, expected_locked) + && features_match(&expected_features, features) + }) .times(metadata_by_path.len()) .returning(move |path, _other_options, _features| { let requested_path = PathBuf::from(path); @@ -1541,4 +1649,33 @@ mod get_target_arch_from_cargo_rustc { }) }); } + + #[test] + fn probe_forwards_features() { + let cwd = PathBuf::from(r"C:\tmp"); + let mut harness = BuildActionHarness::new( + cwd.clone(), + None, + None, + SignMode::Test { + verify_signature: false, + }, + false, + ) + .with_features(super::non_default_features()); + // `expect_cargo_rustc_print_cfg` derives the expected args from + // `harness.features`, so this only matches if the selected features are + // forwarded into the `cargo rustc` probe invocation. + expect_cargo_rustc_print_cfg( + &mut harness, + cwd.clone(), + b"target_arch=\"x86_64\"\n".to_vec(), + ); + + let build_action = initialize_build_action(&mut harness); + let arch = build_action + .get_target_arch_from_cargo_rustc(&cwd) + .expect("Expected target arch to be detected"); + assert_eq!(arch, CpuArchitecture::Amd64); + } } From 540150b5855c0b2fefcf4222330b3c1270fd2e24 Mon Sep 17 00:00:00 2001 From: krishnakumar4a4 Date: Thu, 16 Jul 2026 12:21:28 +0530 Subject: [PATCH 09/12] refactor: add test to skip signing and verification when sign mode is off. This replaces the earlier removed test "given_a_driver_project_when_sign_mode_is_off_then_signing_and_verification_steps_are_skipped" --- .../src/actions/build/package_task.rs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/crates/cargo-wdk/src/actions/build/package_task.rs b/crates/cargo-wdk/src/actions/build/package_task.rs index 35d10bc2e..3421bc1b2 100644 --- a/crates/cargo-wdk/src/actions/build/package_task.rs +++ b/crates/cargo-wdk/src/actions/build/package_task.rs @@ -892,6 +892,55 @@ mod tests { ); } + #[test] + fn run_skips_signing_and_verification_when_sign_mode_is_off() { + let _lock = TEST_MUTEX + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let mut harness = PackageTaskHarness::new().with_sign_mode(SignMode::Off); + let paths = harness.paths(); + + // With SignMode::Off, certificate generation and signtool sign/verify are + // skipped. No expectations are set for those commands, so any attempt to + // run them would fail the test as an unmatched mock expectation. + harness + .expect_exists(paths.src_inx_file_path.clone(), true) + .expect_exists(paths.dest_root_package_folder.clone(), false) + .expect_create_dir_ok(paths.dest_root_package_folder.clone()) + .expect_rename( + paths.src_driver_binary_file_path, + paths.src_renamed_driver_binary_file_path.clone(), + Ok(()), + ) + .expect_copy( + paths.src_renamed_driver_binary_file_path, + paths.dest_driver_binary_path, + Ok(1), + ) + .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) + .expect_copy( + paths.src_inx_file_path, + paths.dest_inf_file_path.clone(), + Ok(1), + ) + .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) + .expect_stampinf( + paths.dest_inf_file_path.clone(), + CpuArchitecture::Amd64, + Some(String::from("1.33")), + ) + .expect_inf2cat(paths.dest_root_package_folder, "10_x64") + .expect_infverif(paths.dest_inf_file_path, "/w", None); + + let task = harness.task(); + let result = task.run(); + + assert!( + result.is_ok(), + "package task failed unexpectedly: {result:?}" + ); + } + #[test] fn run_exports_certificate_from_store_when_it_already_exists() { let _lock = TEST_MUTEX From b64a1c5d6cd27803dbc55e1f3bd1a29d5fe0c087 Mon Sep 17 00:00:00 2001 From: krishnakumar4a4 Date: Thu, 16 Jul 2026 13:53:31 +0530 Subject: [PATCH 10/12] refactor: add missing tests for WDK metadata parsing errors and enhance metadata handling --- .../src/actions/build/package_task.rs | 290 +++++++++++++++++ crates/cargo-wdk/src/actions/build/tests.rs | 296 +++++++++++++++++- 2 files changed, 584 insertions(+), 2 deletions(-) diff --git a/crates/cargo-wdk/src/actions/build/package_task.rs b/crates/cargo-wdk/src/actions/build/package_task.rs index 3421bc1b2..7f2b9c4a7 100644 --- a/crates/cargo-wdk/src/actions/build/package_task.rs +++ b/crates/cargo-wdk/src/actions/build/package_task.rs @@ -1129,6 +1129,201 @@ mod tests { assert!(matches!(result, Err(PackageTaskError::Inf2CatCommand(_)))); } + #[test] + fn run_returns_error_when_infverif_fails() { + let _lock = TEST_MUTEX + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let mut harness = PackageTaskHarness::new(); + let paths = harness.paths(); + + harness + .expect_exists(paths.src_inx_file_path.clone(), true) + .expect_exists(paths.dest_root_package_folder.clone(), true) + .expect_rename( + paths.src_driver_binary_file_path, + paths.src_renamed_driver_binary_file_path.clone(), + Ok(()), + ) + .expect_copy( + paths.src_renamed_driver_binary_file_path, + paths.dest_driver_binary_path, + Ok(1), + ) + .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) + .expect_copy( + paths.src_inx_file_path, + paths.dest_inf_file_path.clone(), + Ok(1), + ) + .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) + .expect_stampinf( + paths.dest_inf_file_path.clone(), + CpuArchitecture::Amd64, + Some(String::from("1.33")), + ) + .expect_inf2cat(paths.dest_root_package_folder, "10_x64") + .expect_infverif_error(paths.dest_inf_file_path, "/w", None); + + let task = harness.task(); + let result = task.run(); + + assert!(matches!( + result, + Err(PackageTaskError::InfVerificationCommand(_)) + )); + } + + #[test] + fn run_returns_error_when_certmgr_fails() { + let _lock = TEST_MUTEX + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let mut harness = PackageTaskHarness::new(); + let paths = harness.paths(); + + // Packaging succeeds up to infverif; certificate generation then queries the + // store via certmgr, which fails here. + harness + .expect_exists(paths.src_inx_file_path.clone(), true) + .expect_exists(paths.dest_root_package_folder.clone(), true) + .expect_rename( + paths.src_driver_binary_file_path, + paths.src_renamed_driver_binary_file_path.clone(), + Ok(()), + ) + .expect_copy( + paths.src_renamed_driver_binary_file_path, + paths.dest_driver_binary_path, + Ok(1), + ) + .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) + .expect_copy( + paths.src_inx_file_path, + paths.dest_inf_file_path.clone(), + Ok(1), + ) + .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) + .expect_stampinf( + paths.dest_inf_file_path.clone(), + CpuArchitecture::Amd64, + Some(String::from("1.33")), + ) + .expect_inf2cat(paths.dest_root_package_folder, "10_x64") + .expect_infverif(paths.dest_inf_file_path, "/w", None) + .expect_exists(paths.src_cert_file_path, false) + .expect_certmgr_exists_check_error(); + + let task = harness.task(); + let result = task.run(); + + assert!(matches!( + result, + Err(PackageTaskError::VerifyCertExistsInStoreCommand(_)) + )); + } + + #[test] + fn run_returns_error_when_makecert_fails() { + let _lock = TEST_MUTEX + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let mut harness = PackageTaskHarness::new(); + let paths = harness.paths(); + + // No cert on disk and none in the store, so the task falls through to + // makecert, which fails here. + harness + .expect_exists(paths.src_inx_file_path.clone(), true) + .expect_exists(paths.dest_root_package_folder.clone(), true) + .expect_rename( + paths.src_driver_binary_file_path, + paths.src_renamed_driver_binary_file_path.clone(), + Ok(()), + ) + .expect_copy( + paths.src_renamed_driver_binary_file_path, + paths.dest_driver_binary_path, + Ok(1), + ) + .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) + .expect_copy( + paths.src_inx_file_path, + paths.dest_inf_file_path.clone(), + Ok(1), + ) + .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) + .expect_stampinf( + paths.dest_inf_file_path.clone(), + CpuArchitecture::Amd64, + Some(String::from("1.33")), + ) + .expect_inf2cat(paths.dest_root_package_folder, "10_x64") + .expect_infverif(paths.dest_inf_file_path, "/w", None) + .expect_exists(paths.src_cert_file_path.clone(), false) + .expect_certmgr_exists_check(false) + .expect_certmgr_exists_check(false) + .expect_makecert_error(paths.src_cert_file_path); + + let task = harness.task(); + let result = task.run(); + + assert!(matches!( + result, + Err(PackageTaskError::CertGenerationInStoreCommand(_)) + )); + } + + #[test] + fn run_returns_error_when_signtool_fails() { + let _lock = TEST_MUTEX + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let mut harness = PackageTaskHarness::new(); + let paths = harness.paths(); + + // Certificate already on disk, so signing proceeds directly; signtool fails + // on the driver binary. + harness + .expect_exists(paths.src_inx_file_path.clone(), true) + .expect_exists(paths.dest_root_package_folder.clone(), true) + .expect_rename( + paths.src_driver_binary_file_path, + paths.src_renamed_driver_binary_file_path.clone(), + Ok(()), + ) + .expect_copy( + paths.src_renamed_driver_binary_file_path, + paths.dest_driver_binary_path.clone(), + Ok(1), + ) + .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) + .expect_copy( + paths.src_inx_file_path, + paths.dest_inf_file_path.clone(), + Ok(1), + ) + .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) + .expect_stampinf( + paths.dest_inf_file_path.clone(), + CpuArchitecture::Amd64, + Some(String::from("1.33")), + ) + .expect_inf2cat(paths.dest_root_package_folder, "10_x64") + .expect_infverif(paths.dest_inf_file_path, "/w", None) + .expect_exists(paths.src_cert_file_path.clone(), true) + .expect_copy(paths.src_cert_file_path, paths.dest_cert_file_path, Ok(1)) + .expect_signtool_sign_error(paths.dest_driver_binary_path); + + let task = harness.task(); + let result = task.run(); + + assert!(matches!( + result, + Err(PackageTaskError::DriverBinarySignCommand(_)) + )); + } + #[test] fn run_skips_infverif_for_samples_when_wdk_build_is_in_bugged_range() { let _lock = TEST_MUTEX @@ -1748,6 +1943,101 @@ mod tests { self } + fn expect_certmgr_exists_check_error(&mut self) -> &mut Self { + self.command_exec + .expect_run() + .withf(|command, args, _, _| { + command == "certmgr.exe" && args == ["-s", WDR_TEST_CERT_STORE] + }) + .once() + .return_once(|_, _, _, _| Err(command_error("certmgr.exe"))); + self + } + + fn expect_makecert_error(&mut self, cert_path: PathBuf) -> &mut Self { + let cert_path = cert_path.to_string_lossy().to_string(); + self.command_exec + .expect_run() + .withf(move |command, args, _, _| { + command == "makecert" + && args + == [ + "-r", + "-pe", + "-a", + "SHA256", + "-eku", + "1.3.6.1.5.5.7.3.3", + "-ss", + WDR_TEST_CERT_STORE, + "-n", + "CN=WDRLocalTestCert", + cert_path.as_str(), + ] + }) + .once() + .return_once(|_, _, _, _| Err(command_error("makecert"))); + self + } + + fn expect_signtool_sign_error(&mut self, file_path: PathBuf) -> &mut Self { + let file_path = file_path.to_string_lossy().to_string(); + self.command_exec + .expect_run() + .withf(move |command, args, _, _| { + command == "signtool" + && args + == [ + "sign", + "/v", + "/s", + WDR_TEST_CERT_STORE, + "/n", + WDR_LOCAL_TEST_CERT, + "/t", + "http://timestamp.digicert.com", + "/fd", + "SHA256", + file_path.as_str(), + ] + }) + .once() + .return_once(|_, _, _, _| Err(command_error("signtool"))); + self + } + + fn expect_infverif_error( + &mut self, + inf_path: PathBuf, + driver_flag: &str, + sample_flag: Option<&'static str>, + ) -> &mut Self { + let inf_path = inf_path.to_string_lossy().to_string(); + let expected_args = if let Some(sample_flag) = sample_flag { + vec![ + "/v".to_string(), + driver_flag.to_string(), + sample_flag.to_string(), + inf_path, + ] + } else { + vec!["/v".to_string(), driver_flag.to_string(), inf_path] + }; + self.command_exec + .expect_run() + .withf(move |command, args, _, _| { + command == "infverif" + && args.len() == expected_args.len() + && args + .iter() + .zip(expected_args.iter()) + .all(|(actual, expected)| actual == expected) + }) + .once() + .return_once(|_, _, _, _| Err(command_error("infverif"))); + self + } + fn expect_wdk_build_number(&mut self, build_number: u32) -> &mut Self { self.wdk_build .expect_detect_wdk_build_number() diff --git a/crates/cargo-wdk/src/actions/build/tests.rs b/crates/cargo-wdk/src/actions/build/tests.rs index 650d5647e..eb1cd71bb 100644 --- a/crates/cargo-wdk/src/actions/build/tests.rs +++ b/crates/cargo-wdk/src/actions/build/tests.rs @@ -16,7 +16,7 @@ use std::{ use cargo_metadata::{Message, Metadata as CargoMetadata}; use clap_cargo::Features; use mockall::predicate::eq; -use wdk_build::{CpuArchitecture, DriverConfig}; +use wdk_build::{CpuArchitecture, DriverConfig, metadata::TryFromCargoMetadataError}; use super::{ BuildAction, @@ -294,6 +294,36 @@ mod standalone_driver_project { "build action failed unexpectedly: {result:?}" ); } + + #[test] + fn run_returns_wdk_metadata_parse_error_for_invalid_metadata() { + let cwd = PathBuf::from(r"C:\tmp\sample-driver"); + let mut harness = BuildActionHarness::new( + cwd.clone(), + None, + None, + SignMode::Test { + verify_signature: false, + }, + false, + ) + .set_up_with_custom_toml(&invalid_driver_cargo_toml()); + + // The package still builds; packaging is skipped because the WDK metadata is + // invalid, and the parse error is surfaced after the build. + harness.expect_build_runner(DEFAULT_DRIVER_NAME, &cwd, None, None, Ok(Vec::new())); + harness.mock_package_task_runner.expect_run().never(); + + let build_action = initialize_build_action(&mut harness); + let result = build_action.run_from_workspace_root(&cwd); + + assert!(matches!( + result, + Err(BuildActionError::WdkMetadataParse( + TryFromCargoMetadataError::WdkMetadataDeserialization { .. } + )) + )); + } } mod driver_workspace { @@ -493,6 +523,185 @@ mod driver_workspace { assert!(matches!(result, Err(BuildActionError::BuildTask(_)))); } + + #[test] + fn run_returns_wdk_metadata_parse_error_for_conflicting_member_configs() { + let workspace_root = PathBuf::from(r"C:\tmp\workspace"); + let driver_name_1 = "sample-kmdf-1"; + let driver_name_2 = "sample-kmdf-2"; + let driver_dir_1 = workspace_root.join(driver_name_1); + let driver_dir_2 = workspace_root.join(driver_name_2); + let mut harness = BuildActionHarness::new( + workspace_root.clone(), + None, + None, + SignMode::Test { + verify_signature: false, + }, + false, + ) + .set_up_workspace_with_multiple_driver_projects( + &workspace_root, + vec![ + ( + driver_name_1, + driver_dir_1.clone(), + Some(get_cargo_metadata_wdk_metadata("KMDF", 1, 33)), + "cdylib", + "cdylib", + "main.rs", + ), + ( + driver_name_2, + driver_dir_2.clone(), + Some(get_cargo_metadata_wdk_metadata("KMDF", 1, 35)), + "cdylib", + "cdylib", + "main.rs", + ), + ], + ); + + // Each member still builds; packaging is skipped for all of them because the + // distinct WDK configurations can't be reconciled, and the parse error is + // surfaced after the build loop. + harness.expect_build_runner(driver_name_1, &driver_dir_1, None, None, Ok(Vec::new())); + harness.expect_build_runner(driver_name_2, &driver_dir_2, None, None, Ok(Vec::new())); + harness.mock_package_task_runner.expect_run().never(); + + let build_action = initialize_build_action(&mut harness); + let result = build_action.run_from_workspace_root(&workspace_root); + + assert!(matches!( + result, + Err(BuildActionError::WdkMetadataParse( + TryFromCargoMetadataError::MultipleWdkConfigurationsDetected { .. } + )) + )); + } + + #[test] + fn run_returns_wdk_metadata_parse_error_for_conflicting_root_and_member_configs() { + let workspace_root = PathBuf::from(r"C:\tmp\workspace"); + let driver_name = "sample-kmdf-1"; + let driver_dir = workspace_root.join(driver_name); + let mut harness = BuildActionHarness::new( + workspace_root.clone(), + None, + None, + SignMode::Test { + verify_signature: false, + }, + false, + ) + .set_up_workspace_with_root_metadata( + &workspace_root, + Some(get_cargo_metadata_wdk_metadata("UMDF", 2, 33)), + vec![( + driver_name, + driver_dir.clone(), + Some(get_cargo_metadata_wdk_metadata("KMDF", 1, 33)), + "cdylib", + "cdylib", + "main.rs", + )], + ); + + // The member builds; packaging is skipped because the root and member WDK + // configurations conflict, and the parse error is surfaced after the build. + harness.expect_build_runner(driver_name, &driver_dir, None, None, Ok(Vec::new())); + harness.mock_package_task_runner.expect_run().never(); + + let build_action = initialize_build_action(&mut harness); + let result = build_action.run_from_workspace_root(&workspace_root); + + assert!(matches!( + result, + Err(BuildActionError::WdkMetadataParse( + TryFromCargoMetadataError::MultipleWdkConfigurationsDetected { .. } + )) + )); + } + + #[test] + fn run_builds_non_driver_only_workspace_from_root() { + let workspace_root = PathBuf::from(r"C:\tmp\workspace"); + let non_driver_name = "non-driver"; + let non_driver_dir = workspace_root.join(non_driver_name); + let mut harness = BuildActionHarness::new( + workspace_root.clone(), + None, + None, + SignMode::Test { + verify_signature: false, + }, + false, + ) + .set_up_workspace_with_multiple_driver_projects( + &workspace_root, + vec![( + non_driver_name, + non_driver_dir.clone(), + None, + "lib", + "lib", + "lib.rs", + )], + ); + + // No WDK configuration anywhere; the non-driver member builds and packaging + // is skipped, so the run succeeds. + harness.expect_build_runner(non_driver_name, &non_driver_dir, None, None, Ok(Vec::new())); + harness.mock_package_task_runner.expect_run().never(); + + let build_action = initialize_build_action(&mut harness); + let result = build_action.run_from_workspace_root(&workspace_root); + + assert!( + result.is_ok(), + "build action failed unexpectedly: {result:?}" + ); + } + + #[test] + fn run_builds_non_driver_only_workspace_from_member() { + let workspace_root = PathBuf::from(r"C:\tmp\workspace"); + let non_driver_name = "non-driver"; + let non_driver_dir = workspace_root.join(non_driver_name); + let mut harness = BuildActionHarness::new( + non_driver_dir.clone(), + None, + None, + SignMode::Test { + verify_signature: false, + }, + false, + ) + .set_up_workspace_with_multiple_driver_projects( + &workspace_root, + vec![( + non_driver_name, + non_driver_dir.clone(), + None, + "lib", + "lib", + "lib.rs", + )], + ); + + // cwd is the non-driver member; it builds, packaging is skipped, and the run + // succeeds. + harness.expect_build_runner(non_driver_name, &non_driver_dir, None, None, Ok(Vec::new())); + harness.mock_package_task_runner.expect_run().never(); + + let build_action = initialize_build_action(&mut harness); + let result = build_action.run_from_workspace_root(&non_driver_dir); + + assert!( + result.is_ok(), + "build action failed unexpectedly: {result:?}" + ); + } } mod emulated_workspace { @@ -807,6 +1016,28 @@ impl BuildActionHarness { self } + fn set_up_workspace_with_root_metadata( + mut self, + workspace_root_dir: &Path, + root_metadata: Option, + packages: Vec>, + ) -> Self { + let cargo_toml_metadata = + metadata_from_packages_with_root(workspace_root_dir, root_metadata, packages); + let cargo_toml_metadata_for_closure = cargo_toml_metadata; + let expected_locked = self.locked; + let expected_features = self.features.clone(); + self.mock_metadata_provider + .expect_get_cargo_metadata_at_path() + .withf(move |_working_dir, other_options, features| { + forwards_locked(other_options, expected_locked) + && features_match(&expected_features, features) + }) + .once() + .returning(move |_, _, _| Ok(cargo_toml_metadata_for_closure.clone())); + self + } + fn set_up_with_custom_toml(mut self, cargo_toml_metadata: &str) -> Self { let cargo_toml_metadata = serde_json::from_str::(cargo_toml_metadata) .expect("failed to parse cargo metadata"); @@ -1037,6 +1268,67 @@ impl Drop for TestWorkspaceRoot { fn metadata_from_packages( workspace_root_dir: &Path, packages: Vec>, +) -> CargoMetadata { + metadata_from_packages_with_root(workspace_root_dir, None, packages) +} + +fn invalid_driver_cargo_toml() -> String { + // A single cdylib driver package whose workspace-level `wdk.driver-model` is + // missing required fields (e.g. `kmdf-version-major`), so `Wdk::try_from` + // fails with `WdkMetadataDeserialization`. + r#" + { + "target_directory": "C:\\tmp\\sample-driver\\target", + "workspace_root": "C:\\tmp\\sample-driver", + "version": 1, + "packages": [ + { + "name": "sample-driver", + "version": "0.0.1", + "id": "path+file:///C:/tmp/sample-driver#0.0.1", + "dependencies": [], + "targets": [ + { + "kind": ["cdylib"], + "crate_types": ["cdylib"], + "name": "sample_driver", + "src_path": "C:\\tmp\\sample-driver\\src\\lib.rs", + "edition": "2021", + "doc": true, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "C:\\tmp\\sample-driver\\Cargo.toml", + "authors": [], + "categories": [], + "keywords": [], + "edition": "2021", + "metadata": { + "wdk": {} + } + } + ], + "workspace_members": [ + "path+file:///C:/tmp/sample-driver#0.0.1" + ], + "metadata": { + "wdk": { + "driver-model": { + "driver-type": "KMDF" + } + } + } + } + "# + .to_string() +} + +fn metadata_from_packages_with_root( + workspace_root_dir: &Path, + root_metadata: Option, + packages: Vec>, ) -> CargoMetadata { let mut package_json = Vec::new(); let mut workspace_member_ids = Vec::new(); @@ -1057,7 +1349,7 @@ fn metadata_from_packages( workspace_root_dir, package_json, &workspace_member_ids, - None, + root_metadata, )) .expect("failed to parse cargo metadata") } From 4de28a38b1f8a4f79c2b4b713be703c98f69d05b Mon Sep 17 00:00:00 2001 From: krishnakumar4a4 Date: Thu, 16 Jul 2026 14:12:11 +0530 Subject: [PATCH 11/12] refactor: streamline packaging tests by consolidating expectations and enhancing readability --- .../src/actions/build/package_task.rs | 482 +++++------------- 1 file changed, 117 insertions(+), 365 deletions(-) diff --git a/crates/cargo-wdk/src/actions/build/package_task.rs b/crates/cargo-wdk/src/actions/build/package_task.rs index 7f2b9c4a7..dd5e31880 100644 --- a/crates/cargo-wdk/src/actions/build/package_task.rs +++ b/crates/cargo-wdk/src/actions/build/package_task.rs @@ -817,62 +817,21 @@ mod tests { #[test] fn run_packages_driver_with_expected_operations() { - let _lock = TEST_MUTEX - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); + let _lock = lock_test(); let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); harness - .expect_exists(paths.src_inx_file_path.clone(), true) - .expect_exists(paths.dest_root_package_folder.clone(), false) - .expect_create_dir_ok(paths.dest_root_package_folder.clone()) - .expect_rename( - paths.src_driver_binary_file_path, - paths.src_renamed_driver_binary_file_path.clone(), - Ok(()), - ) - .expect_copy( - paths.src_renamed_driver_binary_file_path, - paths.dest_driver_binary_path.clone(), - Ok(1), - ) - .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) - .expect_copy( - paths.src_inx_file_path, - paths.dest_inf_file_path.clone(), - Ok(1), - ) - .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) - .expect_stampinf( - paths.dest_inf_file_path.clone(), - CpuArchitecture::Amd64, - Some(String::from("1.33")), - ) - .expect_inf2cat(paths.dest_root_package_folder, "10_x64") - .expect_exists(paths.src_cert_file_path.clone(), false) - .expect_certmgr_exists_check(false) - .expect_certmgr_exists_check(false) - .expect_makecert(paths.src_cert_file_path.clone()) - .expect_copy(paths.src_cert_file_path, paths.dest_cert_file_path, Ok(1)) - .expect_signtool_sign(paths.dest_driver_binary_path) - .expect_signtool_sign(paths.dest_cat_file_path) + .expect_prepare_inf(false) + .expect_generate_self_signed_cert_and_sign() .expect_infverif(paths.dest_inf_file_path, "/w", None); - let task = harness.task(); - let result = task.run(); - - assert!( - result.is_ok(), - "package task failed unexpectedly: {result:?}" - ); + harness.run_and_expect_success(); } #[test] fn run_verifies_signatures_when_enabled() { - let _lock = TEST_MUTEX - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); + let _lock = lock_test(); let mut harness = PackageTaskHarness::new().with_sign_mode(SignMode::Test { verify_signature: true, }); @@ -883,20 +842,12 @@ mod tests { .expect_signtool_verify(paths.dest_driver_binary_path) .expect_signtool_verify(paths.dest_cat_file_path); - let task = harness.task(); - let result = task.run(); - - assert!( - result.is_ok(), - "package task failed unexpectedly: {result:?}" - ); + harness.run_and_expect_success(); } #[test] fn run_skips_signing_and_verification_when_sign_mode_is_off() { - let _lock = TEST_MUTEX - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); + let _lock = lock_test(); let mut harness = PackageTaskHarness::new().with_sign_mode(SignMode::Off); let paths = harness.paths(); @@ -904,77 +855,20 @@ mod tests { // skipped. No expectations are set for those commands, so any attempt to // run them would fail the test as an unmatched mock expectation. harness - .expect_exists(paths.src_inx_file_path.clone(), true) - .expect_exists(paths.dest_root_package_folder.clone(), false) - .expect_create_dir_ok(paths.dest_root_package_folder.clone()) - .expect_rename( - paths.src_driver_binary_file_path, - paths.src_renamed_driver_binary_file_path.clone(), - Ok(()), - ) - .expect_copy( - paths.src_renamed_driver_binary_file_path, - paths.dest_driver_binary_path, - Ok(1), - ) - .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) - .expect_copy( - paths.src_inx_file_path, - paths.dest_inf_file_path.clone(), - Ok(1), - ) - .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) - .expect_stampinf( - paths.dest_inf_file_path.clone(), - CpuArchitecture::Amd64, - Some(String::from("1.33")), - ) - .expect_inf2cat(paths.dest_root_package_folder, "10_x64") + .expect_prepare_inf(false) .expect_infverif(paths.dest_inf_file_path, "/w", None); - let task = harness.task(); - let result = task.run(); - - assert!( - result.is_ok(), - "package task failed unexpectedly: {result:?}" - ); + harness.run_and_expect_success(); } #[test] fn run_exports_certificate_from_store_when_it_already_exists() { - let _lock = TEST_MUTEX - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); + let _lock = lock_test(); let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); harness - .expect_exists(paths.src_inx_file_path.clone(), true) - .expect_exists(paths.dest_root_package_folder.clone(), true) - .expect_rename( - paths.src_driver_binary_file_path, - paths.src_renamed_driver_binary_file_path.clone(), - Ok(()), - ) - .expect_copy( - paths.src_renamed_driver_binary_file_path, - paths.dest_driver_binary_path.clone(), - Ok(1), - ) - .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) - .expect_copy( - paths.src_inx_file_path, - paths.dest_inf_file_path.clone(), - Ok(1), - ) - .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) - .expect_stampinf( - paths.dest_inf_file_path.clone(), - CpuArchitecture::Amd64, - Some(String::from("1.33")), - ) - .expect_inf2cat(paths.dest_root_package_folder, "10_x64") + .expect_prepare_inf(true) .expect_exists(paths.src_cert_file_path.clone(), false) .expect_certmgr_exists_check(true) .expect_certmgr_create_cert_from_store(paths.src_cert_file_path.clone()) @@ -983,20 +877,12 @@ mod tests { .expect_signtool_sign(paths.dest_cat_file_path) .expect_infverif(paths.dest_inf_file_path, "/w", None); - let task = harness.task(); - let result = task.run(); - - assert!( - result.is_ok(), - "package task failed unexpectedly: {result:?}" - ); + harness.run_and_expect_success(); } #[test] fn run_returns_error_when_inx_file_is_missing() { - let _lock = TEST_MUTEX - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); + let _lock = lock_test(); let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); harness.expect_exists(paths.src_inx_file_path.clone(), false); @@ -1013,9 +899,7 @@ mod tests { #[test] fn run_returns_error_when_copying_driver_binary_fails() { - let _lock = TEST_MUTEX - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); + let _lock = lock_test(); let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); @@ -1050,37 +934,15 @@ mod tests { #[test] fn run_returns_error_when_stampinf_fails() { - let _lock = TEST_MUTEX - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); + let _lock = lock_test(); let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); - harness - .expect_exists(paths.src_inx_file_path.clone(), true) - .expect_exists(paths.dest_root_package_folder, true) - .expect_rename( - paths.src_driver_binary_file_path, - paths.src_renamed_driver_binary_file_path.clone(), - Ok(()), - ) - .expect_copy( - paths.src_renamed_driver_binary_file_path, - paths.dest_driver_binary_path, - Ok(1), - ) - .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) - .expect_copy( - paths.src_inx_file_path, - paths.dest_inf_file_path.clone(), - Ok(1), - ) - .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) - .expect_stampinf_error( - paths.dest_inf_file_path, - CpuArchitecture::Amd64, - Some(String::from("1.33")), - ); + harness.expect_copy_artifacts(true).expect_stampinf_error( + paths.dest_inf_file_path, + CpuArchitecture::Amd64, + Some(String::from("1.33")), + ); let task = harness.task(); let result = task.run(); @@ -1090,32 +952,12 @@ mod tests { #[test] fn run_returns_error_when_inf2cat_fails() { - let _lock = TEST_MUTEX - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); + let _lock = lock_test(); let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); harness - .expect_exists(paths.src_inx_file_path.clone(), true) - .expect_exists(paths.dest_root_package_folder.clone(), true) - .expect_rename( - paths.src_driver_binary_file_path, - paths.src_renamed_driver_binary_file_path.clone(), - Ok(()), - ) - .expect_copy( - paths.src_renamed_driver_binary_file_path, - paths.dest_driver_binary_path, - Ok(1), - ) - .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) - .expect_copy( - paths.src_inx_file_path, - paths.dest_inf_file_path.clone(), - Ok(1), - ) - .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) + .expect_copy_artifacts(true) .expect_stampinf( paths.dest_inf_file_path, CpuArchitecture::Amd64, @@ -1131,39 +973,15 @@ mod tests { #[test] fn run_returns_error_when_infverif_fails() { - let _lock = TEST_MUTEX - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); + let _lock = lock_test(); let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); - harness - .expect_exists(paths.src_inx_file_path.clone(), true) - .expect_exists(paths.dest_root_package_folder.clone(), true) - .expect_rename( - paths.src_driver_binary_file_path, - paths.src_renamed_driver_binary_file_path.clone(), - Ok(()), - ) - .expect_copy( - paths.src_renamed_driver_binary_file_path, - paths.dest_driver_binary_path, - Ok(1), - ) - .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) - .expect_copy( - paths.src_inx_file_path, - paths.dest_inf_file_path.clone(), - Ok(1), - ) - .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) - .expect_stampinf( - paths.dest_inf_file_path.clone(), - CpuArchitecture::Amd64, - Some(String::from("1.33")), - ) - .expect_inf2cat(paths.dest_root_package_folder, "10_x64") - .expect_infverif_error(paths.dest_inf_file_path, "/w", None); + harness.expect_prepare_inf(true).expect_infverif_error( + paths.dest_inf_file_path, + "/w", + None, + ); let task = harness.task(); let result = task.run(); @@ -1176,40 +994,14 @@ mod tests { #[test] fn run_returns_error_when_certmgr_fails() { - let _lock = TEST_MUTEX - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); + let _lock = lock_test(); let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); // Packaging succeeds up to infverif; certificate generation then queries the // store via certmgr, which fails here. harness - .expect_exists(paths.src_inx_file_path.clone(), true) - .expect_exists(paths.dest_root_package_folder.clone(), true) - .expect_rename( - paths.src_driver_binary_file_path, - paths.src_renamed_driver_binary_file_path.clone(), - Ok(()), - ) - .expect_copy( - paths.src_renamed_driver_binary_file_path, - paths.dest_driver_binary_path, - Ok(1), - ) - .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) - .expect_copy( - paths.src_inx_file_path, - paths.dest_inf_file_path.clone(), - Ok(1), - ) - .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) - .expect_stampinf( - paths.dest_inf_file_path.clone(), - CpuArchitecture::Amd64, - Some(String::from("1.33")), - ) - .expect_inf2cat(paths.dest_root_package_folder, "10_x64") + .expect_prepare_inf(true) .expect_infverif(paths.dest_inf_file_path, "/w", None) .expect_exists(paths.src_cert_file_path, false) .expect_certmgr_exists_check_error(); @@ -1225,40 +1017,14 @@ mod tests { #[test] fn run_returns_error_when_makecert_fails() { - let _lock = TEST_MUTEX - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); + let _lock = lock_test(); let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); // No cert on disk and none in the store, so the task falls through to // makecert, which fails here. harness - .expect_exists(paths.src_inx_file_path.clone(), true) - .expect_exists(paths.dest_root_package_folder.clone(), true) - .expect_rename( - paths.src_driver_binary_file_path, - paths.src_renamed_driver_binary_file_path.clone(), - Ok(()), - ) - .expect_copy( - paths.src_renamed_driver_binary_file_path, - paths.dest_driver_binary_path, - Ok(1), - ) - .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) - .expect_copy( - paths.src_inx_file_path, - paths.dest_inf_file_path.clone(), - Ok(1), - ) - .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) - .expect_stampinf( - paths.dest_inf_file_path.clone(), - CpuArchitecture::Amd64, - Some(String::from("1.33")), - ) - .expect_inf2cat(paths.dest_root_package_folder, "10_x64") + .expect_prepare_inf(true) .expect_infverif(paths.dest_inf_file_path, "/w", None) .expect_exists(paths.src_cert_file_path.clone(), false) .expect_certmgr_exists_check(false) @@ -1276,40 +1042,14 @@ mod tests { #[test] fn run_returns_error_when_signtool_fails() { - let _lock = TEST_MUTEX - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); + let _lock = lock_test(); let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); // Certificate already on disk, so signing proceeds directly; signtool fails // on the driver binary. harness - .expect_exists(paths.src_inx_file_path.clone(), true) - .expect_exists(paths.dest_root_package_folder.clone(), true) - .expect_rename( - paths.src_driver_binary_file_path, - paths.src_renamed_driver_binary_file_path.clone(), - Ok(()), - ) - .expect_copy( - paths.src_renamed_driver_binary_file_path, - paths.dest_driver_binary_path.clone(), - Ok(1), - ) - .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) - .expect_copy( - paths.src_inx_file_path, - paths.dest_inf_file_path.clone(), - Ok(1), - ) - .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) - .expect_stampinf( - paths.dest_inf_file_path.clone(), - CpuArchitecture::Amd64, - Some(String::from("1.33")), - ) - .expect_inf2cat(paths.dest_root_package_folder, "10_x64") + .expect_prepare_inf(true) .expect_infverif(paths.dest_inf_file_path, "/w", None) .expect_exists(paths.src_cert_file_path.clone(), true) .expect_copy(paths.src_cert_file_path, paths.dest_cert_file_path, Ok(1)) @@ -1326,61 +1066,22 @@ mod tests { #[test] fn run_skips_infverif_for_samples_when_wdk_build_is_in_bugged_range() { - let _lock = TEST_MUTEX - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); + let _lock = lock_test(); let mut harness = PackageTaskHarness::new().with_sample_class(true); - let paths = harness.paths(); + // infverif calls detect_wdk_build_number and skips for samples in the bugged + // range, so no infverif command is expected. harness - .expect_exists(paths.src_inx_file_path.clone(), true) - .expect_exists(paths.dest_root_package_folder.clone(), true) - .expect_rename( - paths.src_driver_binary_file_path, - paths.src_renamed_driver_binary_file_path.clone(), - Ok(()), - ) - .expect_copy( - paths.src_renamed_driver_binary_file_path, - paths.dest_driver_binary_path.clone(), - Ok(1), - ) - .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) - .expect_copy( - paths.src_inx_file_path, - paths.dest_inf_file_path.clone(), - Ok(1), - ) - .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) - .expect_stampinf( - paths.dest_inf_file_path, - CpuArchitecture::Amd64, - Some(String::from("1.33")), - ) - .expect_inf2cat(paths.dest_root_package_folder, "10_x64") - .expect_exists(paths.src_cert_file_path.clone(), false) - .expect_certmgr_exists_check(false) - .expect_certmgr_exists_check(false) - .expect_makecert(paths.src_cert_file_path.clone()) - .expect_copy(paths.src_cert_file_path, paths.dest_cert_file_path, Ok(1)) - .expect_signtool_sign(paths.dest_driver_binary_path) - .expect_signtool_sign(paths.dest_cat_file_path) + .expect_prepare_inf(true) + .expect_generate_self_signed_cert_and_sign() .expect_wdk_build_number(25798); - let task = harness.task(); - let result = task.run(); - - assert!( - result.is_ok(), - "package task failed unexpectedly: {result:?}" - ); + harness.run_and_expect_success(); } #[test] fn stampinf_version_overrides_with_env_var() { - let _lock = TEST_MUTEX - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); + let _lock = lock_test(); // verify both with and without the env var set scenarios let scenarios = [ ("env_set", Some("1.2.3.4"), true), @@ -1614,33 +1315,78 @@ mod tests { } impl PackageTaskHarness { - fn expect_successful_packaging_without_verification(&mut self) -> &mut Self { + /// Expects the `.inx` check, the destination-folder check (and its + /// creation when `dest_root_exists` is false), the driver-binary + /// rename, and the copies of every packaged artifact (driver + /// binary, pdb, inf, map). This is the common prefix of every + /// successful packaging run up to (but not including) + /// `stampinf`. + fn expect_copy_artifacts(&mut self, dest_root_exists: bool) -> &mut Self { let paths = self.paths(); self.expect_exists(paths.src_inx_file_path.clone(), true) - .expect_exists(paths.dest_root_package_folder.clone(), true) - .expect_rename( - paths.src_driver_binary_file_path, - paths.src_renamed_driver_binary_file_path.clone(), - Ok(()), - ) - .expect_copy( - paths.src_renamed_driver_binary_file_path, - paths.dest_driver_binary_path.clone(), - Ok(1), - ) - .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) - .expect_copy( - paths.src_inx_file_path, - paths.dest_inf_file_path.clone(), - Ok(1), - ) - .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) - .expect_stampinf( - paths.dest_inf_file_path.clone(), - CpuArchitecture::Amd64, - Some(String::from("1.33")), - ) - .expect_inf2cat(paths.dest_root_package_folder, "10_x64") + .expect_exists(paths.dest_root_package_folder.clone(), dest_root_exists); + if !dest_root_exists { + self.expect_create_dir_ok(paths.dest_root_package_folder); + } + self.expect_rename( + paths.src_driver_binary_file_path, + paths.src_renamed_driver_binary_file_path.clone(), + Ok(()), + ) + .expect_copy( + paths.src_renamed_driver_binary_file_path, + paths.dest_driver_binary_path, + Ok(1), + ) + .expect_copy(paths.src_pdb_file_path, paths.dest_pdb_file_path, Ok(1)) + .expect_copy(paths.src_inx_file_path, paths.dest_inf_file_path, Ok(1)) + .expect_copy(paths.src_map_file_path, paths.dest_map_file_path, Ok(1)) + } + + /// Expects a successful `stampinf` followed by a successful `inf2cat`. + fn expect_stampinf_and_inf2cat(&mut self) -> &mut Self { + let paths = self.paths(); + self.expect_stampinf( + paths.dest_inf_file_path, + CpuArchitecture::Amd64, + Some(String::from("1.33")), + ) + .expect_inf2cat(paths.dest_root_package_folder, "10_x64") + } + + /// Expects the full inf-preparation pipeline: artifact copies, + /// `stampinf`, and `inf2cat`. + fn expect_prepare_inf(&mut self, dest_root_exists: bool) -> &mut Self { + self.expect_copy_artifacts(dest_root_exists) + .expect_stampinf_and_inf2cat() + } + + /// Expects the self-signed-certificate path (no cert on disk or in the + /// store, so `makecert` generates one) followed by signing the driver + /// binary and catalog file. + fn expect_generate_self_signed_cert_and_sign(&mut self) -> &mut Self { + let paths = self.paths(); + self.expect_exists(paths.src_cert_file_path.clone(), false) + .expect_certmgr_exists_check(false) + .expect_certmgr_exists_check(false) + .expect_makecert(paths.src_cert_file_path.clone()) + .expect_copy(paths.src_cert_file_path, paths.dest_cert_file_path, Ok(1)) + .expect_signtool_sign(paths.dest_driver_binary_path) + .expect_signtool_sign(paths.dest_cat_file_path) + } + + /// Runs the packaging task and asserts it succeeds. + fn run_and_expect_success(&self) { + let result = self.task().run(); + assert!( + result.is_ok(), + "package task failed unexpectedly: {result:?}" + ); + } + + fn expect_successful_packaging_without_verification(&mut self) -> &mut Self { + let paths = self.paths(); + self.expect_prepare_inf(true) .expect_exists(paths.src_cert_file_path.clone(), true) .expect_copy(paths.src_cert_file_path, paths.dest_cert_file_path, Ok(1)) .expect_signtool_sign(paths.dest_driver_binary_path) @@ -2047,6 +1793,12 @@ mod tests { } } + fn lock_test() -> std::sync::MutexGuard<'static, ()> { + TEST_MUTEX + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + } + fn success_output() -> Output { Output { status: ExitStatus::default(), From dcaf613bcce6320caf0ff4b9ae0bb41ce1776d30 Mon Sep 17 00:00:00 2001 From: krishnakumar4a4 Date: Thu, 16 Jul 2026 18:12:52 +0530 Subject: [PATCH 12/12] refactor: enhance test expectations for signing and verification processes in PackageTaskRunner --- .../src/actions/build/package_task.rs | 494 +++++++----------- 1 file changed, 186 insertions(+), 308 deletions(-) diff --git a/crates/cargo-wdk/src/actions/build/package_task.rs b/crates/cargo-wdk/src/actions/build/package_task.rs index dd5e31880..e599cf4f7 100644 --- a/crates/cargo-wdk/src/actions/build/package_task.rs +++ b/crates/cargo-wdk/src/actions/build/package_task.rs @@ -824,7 +824,7 @@ mod tests { harness .expect_prepare_inf(false) .expect_generate_self_signed_cert_and_sign() - .expect_infverif(paths.dest_inf_file_path, "/w", None); + .expect_infverif(paths.dest_inf_file_path, "/w", None, true); harness.run_and_expect_success(); } @@ -854,9 +854,12 @@ mod tests { // With SignMode::Off, certificate generation and signtool sign/verify are // skipped. No expectations are set for those commands, so any attempt to // run them would fail the test as an unmatched mock expectation. - harness - .expect_prepare_inf(false) - .expect_infverif(paths.dest_inf_file_path, "/w", None); + harness.expect_prepare_inf(false).expect_infverif( + paths.dest_inf_file_path, + "/w", + None, + true, + ); harness.run_and_expect_success(); } @@ -870,12 +873,12 @@ mod tests { harness .expect_prepare_inf(true) .expect_exists(paths.src_cert_file_path.clone(), false) - .expect_certmgr_exists_check(true) + .expect_certmgr_exists_check(Ok(certmgr_store_output(true))) .expect_certmgr_create_cert_from_store(paths.src_cert_file_path.clone()) .expect_copy(paths.src_cert_file_path, paths.dest_cert_file_path, Ok(1)) - .expect_signtool_sign(paths.dest_driver_binary_path) - .expect_signtool_sign(paths.dest_cat_file_path) - .expect_infverif(paths.dest_inf_file_path, "/w", None); + .expect_signtool_sign(paths.dest_driver_binary_path, true) + .expect_signtool_sign(paths.dest_cat_file_path, true) + .expect_infverif(paths.dest_inf_file_path, "/w", None, true); harness.run_and_expect_success(); } @@ -938,10 +941,11 @@ mod tests { let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); - harness.expect_copy_artifacts(true).expect_stampinf_error( + harness.expect_copy_artifacts(true).expect_stampinf( paths.dest_inf_file_path, CpuArchitecture::Amd64, Some(String::from("1.33")), + false, ); let task = harness.task(); @@ -962,8 +966,9 @@ mod tests { paths.dest_inf_file_path, CpuArchitecture::Amd64, Some(String::from("1.33")), + true, ) - .expect_inf2cat_error(paths.dest_root_package_folder, "10_x64"); + .expect_inf2cat(paths.dest_root_package_folder, "10_x64", false); let task = harness.task(); let result = task.run(); @@ -977,10 +982,11 @@ mod tests { let mut harness = PackageTaskHarness::new(); let paths = harness.paths(); - harness.expect_prepare_inf(true).expect_infverif_error( + harness.expect_prepare_inf(true).expect_infverif( paths.dest_inf_file_path, "/w", None, + false, ); let task = harness.task(); @@ -1002,9 +1008,9 @@ mod tests { // store via certmgr, which fails here. harness .expect_prepare_inf(true) - .expect_infverif(paths.dest_inf_file_path, "/w", None) + .expect_infverif(paths.dest_inf_file_path, "/w", None, true) .expect_exists(paths.src_cert_file_path, false) - .expect_certmgr_exists_check_error(); + .expect_certmgr_exists_check(Err(command_error("certmgr.exe"))); let task = harness.task(); let result = task.run(); @@ -1025,11 +1031,11 @@ mod tests { // makecert, which fails here. harness .expect_prepare_inf(true) - .expect_infverif(paths.dest_inf_file_path, "/w", None) + .expect_infverif(paths.dest_inf_file_path, "/w", None, true) .expect_exists(paths.src_cert_file_path.clone(), false) - .expect_certmgr_exists_check(false) - .expect_certmgr_exists_check(false) - .expect_makecert_error(paths.src_cert_file_path); + .expect_certmgr_exists_check(Ok(certmgr_store_output(false))) + .expect_certmgr_exists_check(Ok(certmgr_store_output(false))) + .expect_makecert(paths.src_cert_file_path, false); let task = harness.task(); let result = task.run(); @@ -1050,10 +1056,10 @@ mod tests { // on the driver binary. harness .expect_prepare_inf(true) - .expect_infverif(paths.dest_inf_file_path, "/w", None) + .expect_infverif(paths.dest_inf_file_path, "/w", None, true) .expect_exists(paths.src_cert_file_path.clone(), true) .expect_copy(paths.src_cert_file_path, paths.dest_cert_file_path, Ok(1)) - .expect_signtool_sign_error(paths.dest_driver_binary_path); + .expect_signtool_sign(paths.dest_driver_binary_path, false); let task = harness.task(); let result = task.run(); @@ -1350,8 +1356,9 @@ mod tests { paths.dest_inf_file_path, CpuArchitecture::Amd64, Some(String::from("1.33")), + true, ) - .expect_inf2cat(paths.dest_root_package_folder, "10_x64") + .expect_inf2cat(paths.dest_root_package_folder, "10_x64", true) } /// Expects the full inf-preparation pipeline: artifact copies, @@ -1367,12 +1374,12 @@ mod tests { fn expect_generate_self_signed_cert_and_sign(&mut self) -> &mut Self { let paths = self.paths(); self.expect_exists(paths.src_cert_file_path.clone(), false) - .expect_certmgr_exists_check(false) - .expect_certmgr_exists_check(false) - .expect_makecert(paths.src_cert_file_path.clone()) + .expect_certmgr_exists_check(Ok(certmgr_store_output(false))) + .expect_certmgr_exists_check(Ok(certmgr_store_output(false))) + .expect_makecert(paths.src_cert_file_path.clone(), true) .expect_copy(paths.src_cert_file_path, paths.dest_cert_file_path, Ok(1)) - .expect_signtool_sign(paths.dest_driver_binary_path) - .expect_signtool_sign(paths.dest_cat_file_path) + .expect_signtool_sign(paths.dest_driver_binary_path, true) + .expect_signtool_sign(paths.dest_cat_file_path, true) } /// Runs the packaging task and asserts it succeeds. @@ -1389,9 +1396,9 @@ mod tests { self.expect_prepare_inf(true) .expect_exists(paths.src_cert_file_path.clone(), true) .expect_copy(paths.src_cert_file_path, paths.dest_cert_file_path, Ok(1)) - .expect_signtool_sign(paths.dest_driver_binary_path) - .expect_signtool_sign(paths.dest_cat_file_path) - .expect_infverif(paths.dest_inf_file_path, "/w", None) + .expect_signtool_sign(paths.dest_driver_binary_path, true) + .expect_signtool_sign(paths.dest_cat_file_path, true) + .expect_infverif(paths.dest_inf_file_path, "/w", None, true) } fn expect_exists(&mut self, path: PathBuf, exists: bool) -> &mut Self { @@ -1440,41 +1447,37 @@ mod tests { self } - fn expect_stampinf( + /// Sets a single `command_exec.run` expectation matching `command` with + /// an exact argument list, returning `result`. The success and + /// failure cases share this one matcher, differing only in + /// `result`. + fn expect_command( &mut self, - dest_inf_file_path: PathBuf, - arch: CpuArchitecture, - wdf_version: Option, + command: &'static str, + expected_args: Vec, + result: Result, ) -> &mut Self { self.command_exec .expect_run() - .withf(move |command, args, _, _| { - let dest_inf = dest_inf_file_path.to_string_lossy().to_string(); - command == "stampinf" - && args.len() >= 8 - && args[0] == "-f" - && args[1] == dest_inf - && args[2] == "-d" - && args[3] == "*" - && args[4] == "-a" - && args[5] == arch.to_string() - && args[6] == "-c" - && args[7] == "sample_driver.cat" - && args.windows(2).any(|window| window == ["-v", "*"]) - && wdf_version.as_deref().is_none_or(|version| { - args.windows(2).any(|window| window == ["-k", version]) - }) + .withf(move |cmd, args, _, _| { + cmd == command + && args.len() == expected_args.len() + && args + .iter() + .zip(&expected_args) + .all(|(a, e)| *a == e.as_str()) }) .once() - .return_once(|_, _, _, _| Ok(success_output())); + .return_once(move |_, _, _, _| result); self } - fn expect_stampinf_error( + fn expect_stampinf( &mut self, dest_inf_file_path: PathBuf, arch: CpuArchitecture, wdf_version: Option, + succeeds: bool, ) -> &mut Self { self.command_exec .expect_run() @@ -1484,304 +1487,145 @@ mod tests { && args.len() >= 8 && args[0] == "-f" && args[1] == dest_inf + && args[2] == "-d" + && args[3] == "*" && args[4] == "-a" && args[5] == arch.to_string() + && args[6] == "-c" + && args[7] == "sample_driver.cat" + && args.windows(2).any(|window| window == ["-v", "*"]) && wdf_version.as_deref().is_none_or(|version| { args.windows(2).any(|window| window == ["-k", version]) }) }) .once() - .return_once(|_, _, _, _| Err(command_error("stampinf"))); - self - } - - fn expect_inf2cat(&mut self, dest_root: PathBuf, os_mapping: &str) -> &mut Self { - let trimmed_dest_root = dest_root - .to_string_lossy() - .trim_start_matches("\\\\?\\") - .to_string(); - let expected_driver_arg = format!("/driver:{trimmed_dest_root}"); - let expected_os_arg = format!("/os:{os_mapping}"); - self.command_exec - .expect_run() - .withf(move |command, args, _, _| { - command == "inf2cat" - && args - == [ - expected_driver_arg.as_str(), - expected_os_arg.as_str(), - "/uselocaltime", - ] - }) - .once() - .return_once(|_, _, _, _| Ok(success_output())); + .return_once(move |_, _, _, _| command_outcome("stampinf", succeeds)); self } - fn expect_inf2cat_error(&mut self, dest_root: PathBuf, os_mapping: &str) -> &mut Self { + fn expect_inf2cat( + &mut self, + dest_root: PathBuf, + os_mapping: &str, + succeeds: bool, + ) -> &mut Self { let trimmed_dest_root = dest_root .to_string_lossy() .trim_start_matches("\\\\?\\") .to_string(); - let expected_driver_arg = format!("/driver:{trimmed_dest_root}"); - let expected_os_arg = format!("/os:{os_mapping}"); - self.command_exec - .expect_run() - .withf(move |command, args, _, _| { - command == "inf2cat" - && args - == [ - expected_driver_arg.as_str(), - expected_os_arg.as_str(), - "/uselocaltime", - ] - }) - .once() - .return_once(|_, _, _, _| Err(command_error("inf2cat"))); - self + self.expect_command( + "inf2cat", + vec![ + format!("/driver:{trimmed_dest_root}"), + format!("/os:{os_mapping}"), + "/uselocaltime".to_string(), + ], + command_outcome("inf2cat", succeeds), + ) } - fn expect_certmgr_exists_check(&mut self, has_cert: bool) -> &mut Self { - let stdout = if has_cert { - r"==============Certificate # 1 ========== - Subject:: - [0,0] 2.5.4.3 (CN) WDRLocalTestCert - CertMgr Succeeded" - .as_bytes() - .to_vec() - } else { - r"==============No Certificates ========== - CertMgr Succeeded" - .as_bytes() - .to_vec() - }; - self.command_exec - .expect_run() - .withf(|command, args, _, _| { - command == "certmgr.exe" && args == ["-s", WDR_TEST_CERT_STORE] - }) - .once() - .return_once(move |_, _, _, _| { - Ok(Output { - status: ExitStatus::default(), - stdout, - stderr: vec![], - }) - }); - self + fn expect_certmgr_exists_check( + &mut self, + result: Result, + ) -> &mut Self { + self.expect_command( + "certmgr.exe", + vec!["-s".to_string(), WDR_TEST_CERT_STORE.to_string()], + result, + ) } fn expect_certmgr_create_cert_from_store(&mut self, cert_path: PathBuf) -> &mut Self { let cert_path = cert_path.to_string_lossy().to_string(); - self.command_exec - .expect_run() - .withf(move |command, args, _, _| { - command == "certmgr.exe" - && args - == [ - "-put", - "-s", - WDR_TEST_CERT_STORE, - "-c", - "-n", - WDR_LOCAL_TEST_CERT, - cert_path.as_str(), - ] - }) - .once() - .return_once(|_, _, _, _| Ok(success_output())); - self + self.expect_command( + "certmgr.exe", + vec![ + "-put".to_string(), + "-s".to_string(), + WDR_TEST_CERT_STORE.to_string(), + "-c".to_string(), + "-n".to_string(), + WDR_LOCAL_TEST_CERT.to_string(), + cert_path, + ], + Ok(success_output()), + ) } - fn expect_makecert(&mut self, cert_path: PathBuf) -> &mut Self { + fn expect_makecert(&mut self, cert_path: PathBuf, succeeds: bool) -> &mut Self { let cert_path = cert_path.to_string_lossy().to_string(); - self.command_exec - .expect_run() - .withf(move |command, args, _, _| { - command == "makecert" - && args - == [ - "-r", - "-pe", - "-a", - "SHA256", - "-eku", - "1.3.6.1.5.5.7.3.3", - "-ss", - WDR_TEST_CERT_STORE, - "-n", - "CN=WDRLocalTestCert", - cert_path.as_str(), - ] - }) - .once() - .return_once(|_, _, _, _| Ok(success_output())); - self + self.expect_command( + "makecert", + vec![ + "-r".to_string(), + "-pe".to_string(), + "-a".to_string(), + "SHA256".to_string(), + "-eku".to_string(), + "1.3.6.1.5.5.7.3.3".to_string(), + "-ss".to_string(), + WDR_TEST_CERT_STORE.to_string(), + "-n".to_string(), + "CN=WDRLocalTestCert".to_string(), + cert_path, + ], + command_outcome("makecert", succeeds), + ) } - fn expect_signtool_sign(&mut self, file_path: PathBuf) -> &mut Self { + fn expect_signtool_sign(&mut self, file_path: PathBuf, succeeds: bool) -> &mut Self { let file_path = file_path.to_string_lossy().to_string(); - self.command_exec - .expect_run() - .withf(move |command, args, _, _| { - command == "signtool" - && args - == [ - "sign", - "/v", - "/s", - WDR_TEST_CERT_STORE, - "/n", - WDR_LOCAL_TEST_CERT, - "/t", - "http://timestamp.digicert.com", - "/fd", - "SHA256", - file_path.as_str(), - ] - }) - .once() - .return_once(|_, _, _, _| Ok(success_output())); - self + self.expect_command( + "signtool", + vec![ + "sign".to_string(), + "/v".to_string(), + "/s".to_string(), + WDR_TEST_CERT_STORE.to_string(), + "/n".to_string(), + WDR_LOCAL_TEST_CERT.to_string(), + "/t".to_string(), + "http://timestamp.digicert.com".to_string(), + "/fd".to_string(), + "SHA256".to_string(), + file_path, + ], + command_outcome("signtool", succeeds), + ) } fn expect_signtool_verify(&mut self, file_path: PathBuf) -> &mut Self { let file_path = file_path.to_string_lossy().to_string(); - self.command_exec - .expect_run() - .withf(move |command, args, _, _| { - command == "signtool" && args == ["verify", "/v", "/pa", file_path.as_str()] - }) - .once() - .return_once(|_, _, _, _| Ok(success_output())); - self - } - - fn expect_infverif( - &mut self, - inf_path: PathBuf, - driver_flag: &str, - sample_flag: Option<&'static str>, - ) -> &mut Self { - let inf_path = inf_path.to_string_lossy().to_string(); - let expected_args = if let Some(sample_flag) = sample_flag { + self.expect_command( + "signtool", vec![ + "verify".to_string(), "/v".to_string(), - driver_flag.to_string(), - sample_flag.to_string(), - inf_path, - ] - } else { - vec!["/v".to_string(), driver_flag.to_string(), inf_path] - }; - self.command_exec - .expect_run() - .withf(move |command, args, _, _| { - command == "infverif" - && args.len() == expected_args.len() - && args - .iter() - .zip(expected_args.iter()) - .all(|(actual, expected)| actual == expected) - }) - .once() - .return_once(|_, _, _, _| Ok(success_output())); - self - } - - fn expect_certmgr_exists_check_error(&mut self) -> &mut Self { - self.command_exec - .expect_run() - .withf(|command, args, _, _| { - command == "certmgr.exe" && args == ["-s", WDR_TEST_CERT_STORE] - }) - .once() - .return_once(|_, _, _, _| Err(command_error("certmgr.exe"))); - self - } - - fn expect_makecert_error(&mut self, cert_path: PathBuf) -> &mut Self { - let cert_path = cert_path.to_string_lossy().to_string(); - self.command_exec - .expect_run() - .withf(move |command, args, _, _| { - command == "makecert" - && args - == [ - "-r", - "-pe", - "-a", - "SHA256", - "-eku", - "1.3.6.1.5.5.7.3.3", - "-ss", - WDR_TEST_CERT_STORE, - "-n", - "CN=WDRLocalTestCert", - cert_path.as_str(), - ] - }) - .once() - .return_once(|_, _, _, _| Err(command_error("makecert"))); - self - } - - fn expect_signtool_sign_error(&mut self, file_path: PathBuf) -> &mut Self { - let file_path = file_path.to_string_lossy().to_string(); - self.command_exec - .expect_run() - .withf(move |command, args, _, _| { - command == "signtool" - && args - == [ - "sign", - "/v", - "/s", - WDR_TEST_CERT_STORE, - "/n", - WDR_LOCAL_TEST_CERT, - "/t", - "http://timestamp.digicert.com", - "/fd", - "SHA256", - file_path.as_str(), - ] - }) - .once() - .return_once(|_, _, _, _| Err(command_error("signtool"))); - self + "/pa".to_string(), + file_path, + ], + Ok(success_output()), + ) } - fn expect_infverif_error( + fn expect_infverif( &mut self, inf_path: PathBuf, driver_flag: &str, sample_flag: Option<&'static str>, + succeeds: bool, ) -> &mut Self { let inf_path = inf_path.to_string_lossy().to_string(); - let expected_args = if let Some(sample_flag) = sample_flag { - vec![ - "/v".to_string(), - driver_flag.to_string(), - sample_flag.to_string(), - inf_path, - ] - } else { - vec!["/v".to_string(), driver_flag.to_string(), inf_path] - }; - self.command_exec - .expect_run() - .withf(move |command, args, _, _| { - command == "infverif" - && args.len() == expected_args.len() - && args - .iter() - .zip(expected_args.iter()) - .all(|(actual, expected)| actual == expected) - }) - .once() - .return_once(|_, _, _, _| Err(command_error("infverif"))); - self + let mut expected_args = vec!["/v".to_string(), driver_flag.to_string()]; + if let Some(sample_flag) = sample_flag { + expected_args.push(sample_flag.to_string()); + } + expected_args.push(inf_path); + self.expect_command( + "infverif", + expected_args, + command_outcome("infverif", succeeds), + ) } fn expect_wdk_build_number(&mut self, build_number: u32) -> &mut Self { @@ -1807,6 +1651,40 @@ mod tests { } } + /// Builds the `command_exec.run` result for a command expectation: a + /// success output when `succeeds`, otherwise a `CommandError` for + /// `command`. + fn command_outcome(command: &'static str, succeeds: bool) -> Result { + if succeeds { + Ok(success_output()) + } else { + Err(command_error(command)) + } + } + + /// Builds the `certmgr.exe -s` stdout indicating whether the test cert is + /// present in the store. + fn certmgr_store_output(has_cert: bool) -> Output { + let stdout = if has_cert { + r"==============Certificate # 1 ========== + Subject:: + [0,0] 2.5.4.3 (CN) WDRLocalTestCert + CertMgr Succeeded" + .as_bytes() + .to_vec() + } else { + r"==============No Certificates ========== + CertMgr Succeeded" + .as_bytes() + .to_vec() + }; + Output { + status: ExitStatus::default(), + stdout, + stderr: vec![], + } + } + fn command_error(command: &'static str) -> CommandError { CommandError::from_output( command,