#[cfg(test)] mod tests { use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; use crate::process::run::{run, RunError}; use crate::text::dedent; #[test] fn test_run_command() { // Test running a simple echo command using the builder pattern let result = run("echo hello").execute().unwrap(); assert!(result.success); assert_eq!(result.code, 0); assert!(result.stdout.trim().contains("hello")); assert_eq!(result.stderr, ""); } #[test] fn test_run_silent_command() { // Test running a command silently using the builder pattern let result = run("echo silent test").silent(true).execute().unwrap(); assert!(result.success); assert_eq!(result.code, 0); assert!(result.stdout.trim().contains("silent test")); assert_eq!(result.stderr, ""); } #[test] fn test_run_script() { // Test running a multi-line script using the builder pattern let script = r#" echo "line 1" echo "line 2" "#; let result = run(script).execute().unwrap(); assert!(result.success); assert_eq!(result.code, 0); assert!(result.stdout.contains("line 1")); assert!(result.stdout.contains("line 2")); assert_eq!(result.stderr, ""); } #[test] fn test_run_with_dedent() { // Test that run properly dedents scripts let script = r#" echo "This has 12 spaces of indentation" echo "This has 16 spaces (4 more than the common indentation)" "#; // The dedent function should remove the common 12 spaces let dedented = dedent(script); assert!(dedented.contains("echo \"This has 12 spaces of indentation\"")); assert!(dedented.contains(" echo \"This has 16 spaces (4 more than the common indentation)\"")); // Running the script should work with the dedented content let result = run(script).execute().unwrap(); assert!(result.success); assert_eq!(result.code, 0); assert!(result.stdout.contains("This has 12 spaces of indentation")); assert!(result.stdout.contains("This has 16 spaces (4 more than the common indentation)")); } #[test] fn test_run_detects_script_vs_command() { // Test that run correctly identifies scripts vs commands // One-liner should be treated as a command let one_liner = "echo one-liner test"; let result = run(one_liner).execute().unwrap(); assert!(result.success); assert!(result.stdout.contains("one-liner test")); // Multi-line input should be treated as a script let multi_line = "echo first line\necho second line"; let result = run(multi_line).execute().unwrap(); assert!(result.success); assert!(result.stdout.contains("first line")); assert!(result.stdout.contains("second line")); } #[test] fn test_run_empty_command() { // Test handling of empty commands let result = run("").execute(); assert!(result.is_err()); // The specific error should be EmptyCommand match result { Err(RunError::EmptyCommand) => (), _ => panic!("Expected EmptyCommand error"), } } #[test] fn test_run_die_option() { // Test the die option - when false, it should return a CommandResult with success=false // instead of an Err when the command fails // With die=true (default), a non-existent command should return an error let result = run("non_existent_command").execute(); assert!(result.is_err()); // With die=false, it should return a CommandResult with success=false let result = run("non_existent_command").die(false).execute().unwrap(); assert!(!result.success); assert_ne!(result.code, 0); assert!(result.stderr.contains("Error:")); } #[test] fn test_run_async_option() { // Test the async option - when true, it should spawn the process and return immediately // Create a shared variable to track if the command has completed let completed = Arc::new(Mutex::new(false)); let completed_clone = completed.clone(); // Run a command that sleeps for 2 seconds, with async=true let start = std::time::Instant::now(); let result = run("sleep 2").async_exec(true).execute().unwrap(); let elapsed = start.elapsed(); // The command should return immediately (much less than 2 seconds) assert!(elapsed < Duration::from_secs(1)); // The result should have empty stdout/stderr and success=true assert!(result.success); assert_eq!(result.code, 0); assert_eq!(result.stdout, ""); assert_eq!(result.stderr, ""); // Wait a bit to ensure the command has time to complete thread::sleep(Duration::from_secs(3)); // Verify the command completed (this is just a placeholder since we can't easily // check if the async command completed in this test framework) *completed_clone.lock().unwrap() = true; assert!(*completed.lock().unwrap()); } #[test] fn test_run_log_option() { // Test the log option - when true, it should log command execution // Note: We can't easily capture stdout in tests, so this is more of a smoke test // Run a command with log=true let result = run("echo log test").log(true).execute().unwrap(); assert!(result.success); assert_eq!(result.code, 0); assert!(result.stdout.trim().contains("log test")); } #[test] fn test_builder_method_chaining() { // Test that all builder methods can be chained together let result = run("echo chaining test") .silent(true) .die(true) .log(true) .execute() .unwrap(); assert!(result.success); assert_eq!(result.code, 0); assert!(result.stdout.trim().contains("chaining test")); } }