From d092ddc5867e6c111e70ad0e20b35c158823c529 Mon Sep 17 00:00:00 2001 From: Jeff Ithier Date: Tue, 10 Sep 2024 19:38:27 +0200 Subject: [PATCH] Test CLI entrypoint with real filesystem --- iceoryx2-cli/iox2/Cargo.toml | 2 + iceoryx2-cli/iox2/src/commands.rs | 99 ++++++++++++++++++++++++------- 2 files changed, 79 insertions(+), 22 deletions(-) diff --git a/iceoryx2-cli/iox2/Cargo.toml b/iceoryx2-cli/iox2/Cargo.toml index f56f698fc..0e98ec707 100644 --- a/iceoryx2-cli/iox2/Cargo.toml +++ b/iceoryx2-cli/iox2/Cargo.toml @@ -21,3 +21,5 @@ cargo_metadata = "0.18.1" [dev-dependencies] iceoryx2-bb-testing = { workspace = true } tempfile = "3.12.0" +winapi = { version = "0.3", features = ["winnt"] } +pelite = "0.10" diff --git a/iceoryx2-cli/iox2/src/commands.rs b/iceoryx2-cli/iox2/src/commands.rs index 33ee0abe0..127112d34 100644 --- a/iceoryx2-cli/iox2/src/commands.rs +++ b/iceoryx2-cli/iox2/src/commands.rs @@ -41,12 +41,12 @@ pub struct PathsList { #[cfg(windows)] const PATH_SEPARATOR: char = ';'; #[cfg(windows)] -const COMMAND_SUFFIX: &str = ".exe"; +const COMMAND_EXT: &str = "exe"; #[cfg(not(windows))] const PATH_SEPARATOR: char = ':'; #[cfg(not(windows))] -const COMMAND_SUFFIX: &str = ""; +const COMMAND_EXT: &str = ""; pub trait Environment { fn install_paths() -> Result>; @@ -97,23 +97,22 @@ where E: Environment, { fn parse_command_name(path: &PathBuf) -> Result { - path.file_name() + let file_stem = path + .file_stem() .and_then(|os_str| os_str.to_str()) - .ok_or_else(|| anyhow!("Invalid file name")) - .and_then( - |file_name| match path.extension().and_then(|ext| ext.to_str()) { - Some(ext) if ext != COMMAND_SUFFIX => { - Err(anyhow!("File has an invalid extension: {}", file_name)) - } - _ => Ok(file_name), - }, - ) - .and_then(|file_name| { - file_name - .strip_prefix("iox2-") - .map(String::from) - .ok_or_else(|| anyhow!("Not an iox2 command: {}", file_name)) - }) + .ok_or_else(|| anyhow!("Invalid file name"))?; + + let command_name = file_stem + .strip_prefix("iox2-") + .ok_or_else(|| anyhow!("Not an iox2 command: {}", file_stem))?; + + let extension = path.extension().and_then(|ext| ext.to_str()).unwrap_or(""); + + if extension == COMMAND_EXT { + Ok(command_name.to_string()) + } else { + Err(anyhow!("Invalid file extension: {}", extension)) + } } fn list_commands_in_path(path: &Path, command_type: CommandType) -> Result> { @@ -280,6 +279,48 @@ mod tests { const BAR_COMMAND: &str = "m3Qf8RzN"; const BAZ_COMMAND: &str = "P5hJ2wAc"; + #[cfg(windows)] + fn create_minimal_exe(path: &Path) -> Result<(), Box> { + use std::io::Write; + + let mut file = std::fs::File::create(path)?; + + // Create a minimal PE file + let mut image = vec![0; 1024]; + let mut pe = PeFile::from_bytes_mut(&mut image)?; + + // Set up the DOS header + pe.dos_header_mut().e_magic = pelite::image::IMAGE_DOS_SIGNATURE; + pe.dos_header_mut().e_lfanew = 0x40; + + // Set up the NT headers + let nt = pe.nt_headers_mut(); + nt.Signature = pelite::image::IMAGE_NT_SIGNATURE; + nt.FileHeader.Machine = pelite::image::IMAGE_FILE_MACHINE_AMD64; + nt.FileHeader.NumberOfSections = 1; + nt.FileHeader.SizeOfOptionalHeader = 0xF0; + nt.FileHeader.Characteristics = FileCharacteristics::EXECUTABLE_IMAGE.bits(); + + // Set up the Optional header + let opt = &mut nt.OptionalHeader; + opt.Magic = pelite::image::IMAGE_NT_OPTIONAL_HDR64_MAGIC; + opt.SizeOfCode = 0x200; + opt.AddressOfEntryPoint = 0x1000; + opt.BaseOfCode = 0x1000; + opt.ImageBase = 0x140000000; + opt.SectionAlignment = 0x1000; + opt.FileAlignment = 0x200; + opt.MajorSubsystemVersion = 6; + opt.SizeOfImage = 0x2000; + opt.SizeOfHeaders = 0x200; + opt.Subsystem = pelite::image::IMAGE_SUBSYSTEM_WINDOWS_CUI; + + // Write the PE file + file.write_all(&image)?; + + Ok(()) + } + macro_rules! create_file { ($path:expr, $file:expr) => {{ let file_path = $path.join($file); @@ -293,6 +334,12 @@ mod tests { perms.set_mode(0o755); fs::set_permissions(&file_path, perms).expect("Failed to set permissions"); } + #[cfg(windows)] + { + if file_path.extension().and_then(|s| s.to_str()) == Some("exe") { + create_minimal_exe(&file_path).expect("Failed to create minimal executable"); + } + } }}; } @@ -364,19 +411,27 @@ mod tests { let commands = IceoryxCommandFinder::::commands() .expect("Failed to retrieve commands"); - let [foo_command, bar_command, ..] = commands + let [foo_command, ..] = commands .iter() - .filter(|cmd| [FOO_COMMAND, BAR_COMMAND].contains(&cmd.name.as_str())) + .filter(|cmd| cmd.name == FOO_COMMAND) .collect::>()[..] else { - panic!("Failed to find FOO_COMMAND and BAR_COMMAND"); + panic!("Failed to extract CommandInfo of test files"); }; let result = IceoryxCommandExecutor::execute(&foo_command, None); + match result { + Ok(_) => { + println!("Command executed successfully"); + } + Err(ref e) => { + println!("Command execution failed with error: {:?}", e); + } + } assert_that!(result, is_ok); let args = vec!["arg1".to_string(), "arg2".to_string()]; - let result = IceoryxCommandExecutor::execute(&bar_command, Some(&args)); + let result = IceoryxCommandExecutor::execute(&foo_command, Some(&args)); assert_that!(result, is_ok); } }