use crate::cmd;
use crate::io_ext::ReadExt;
use std::io::{BufRead, BufReader, Cursor, Read, Write};
use std::thread;
#[test]
fn test_pattern_000_basic_execution() {
let result = cmd!("echo", "hello").no_echo().run();
assert!(result.is_ok());
let output = cmd!("echo", "basic_test").no_echo().output().unwrap();
assert_eq!(output.trim(), "basic_test");
let output = cmd!("echo", "world")
.pipe(cmd!("grep", "world"))
.no_echo()
.output()
.unwrap();
assert_eq!(output.trim(), "world");
}
#[test]
fn test_pattern_100_stdin_only() {
let (handle, stdin) = cmd!("wc", "-l").no_echo().spawn_io_in().unwrap();
let input_handle = stdin.map(|mut stdin| {
thread::spawn(move || {
stdin.write_all(b"line1\nline2\nline3\n").unwrap();
})
});
if let Some(h) = input_handle {
h.join().unwrap();
}
let result = handle.wait();
assert!(result.is_ok());
}
#[test]
fn test_pattern_010_stdout_only() {
let (handle, stdout) = cmd!("seq", "1", "3").no_echo().spawn_io_out().unwrap();
let output_handle = stdout.map(|stdout| {
thread::spawn(move || {
let mut buffer = Vec::new();
let mut reader = BufReader::new(stdout);
reader.read_to_end(&mut buffer).unwrap();
String::from_utf8_lossy(&buffer).trim().to_string()
})
});
handle.wait().unwrap();
if let Some(h) = output_handle {
let output = h.join().unwrap();
assert_eq!(output, "1\n2\n3");
}
}
#[test]
fn test_pattern_001_stderr_only() {
let (handle, stderr) = cmd!("sh", "-c", "echo 'error message' >&2")
.no_echo()
.spawn_io_err()
.unwrap();
let error_handle = stderr.map(|stderr| {
thread::spawn(move || {
let mut buffer = Vec::new();
let mut reader = BufReader::new(stderr);
reader.read_to_end(&mut buffer).unwrap();
String::from_utf8_lossy(&buffer).trim().to_string()
})
});
handle.wait().unwrap();
if let Some(h) = error_handle {
let error_output = h.join().unwrap();
assert_eq!(error_output, "error message");
}
}
#[test]
fn test_pattern_110_stdin_stdout_interactive() {
let (handle, stdin, stdout) = cmd!("tr", "a-z", "A-Z")
.no_echo()
.spawn_io_in_out()
.unwrap();
let input_handle = stdin.map(|mut stdin| {
thread::spawn(move || {
stdin.write_all(b"hello world").unwrap();
})
});
let output_handle = stdout.map(|stdout| {
thread::spawn(move || {
let mut buffer = Vec::new();
let mut reader = BufReader::new(stdout);
reader.read_to_end(&mut buffer).unwrap();
String::from_utf8_lossy(&buffer).trim().to_string()
})
});
if let Some(h) = input_handle {
h.join().unwrap();
}
handle.wait().unwrap();
if let Some(h) = output_handle {
let output = h.join().unwrap();
assert_eq!(output, "HELLO WORLD");
}
let (handle, stdin, stdout) = cmd!("bc", "-l").no_echo().spawn_io_in_out().unwrap();
let input_handle = stdin.map(|mut stdin| {
thread::spawn(move || {
stdin.write_all(b"2+2\nquit\n").unwrap();
})
});
let output_handle = stdout.map(|stdout| {
thread::spawn(move || {
let reader = BufReader::new(stdout);
let mut results = Vec::new();
#[allow(clippy::manual_flatten)]
for line in reader.lines() {
if let Ok(line) = line {
let trimmed = line.trim();
if !trimmed.is_empty() && !trimmed.contains("(standard_in)") {
results.push(trimmed.to_string());
}
}
}
results
})
});
if let Some(h) = input_handle {
h.join().unwrap();
}
handle.wait().unwrap();
if let Some(h) = output_handle {
let results = h.join().unwrap();
assert!(!results.is_empty());
assert_eq!(results.first().unwrap_or(&"".to_string()), "4");
}
}
#[test]
fn test_pattern_101_stdin_stderr_debug() {
let (handle, stdin, stderr) = cmd!("rustc", "-").no_echo().spawn_io_in_err().unwrap();
let input_handle = stdin.map(|mut stdin| {
thread::spawn(move || {
stdin
.write_all(b"fn main() { let x: i32 = \"invalid\"; }")
.unwrap();
})
});
let error_handle = stderr.map(|stderr| {
thread::spawn(move || {
let reader = BufReader::new(stderr);
let mut error_count = 0;
#[allow(clippy::manual_flatten)]
for line in reader.lines() {
if let Ok(line) = line {
if line.contains("error") {
error_count += 1;
}
}
}
error_count
})
});
if let Some(h) = input_handle {
h.join().unwrap();
}
let _ = handle.wait();
if let Some(h) = error_handle {
let error_count = h.join().unwrap();
assert!(error_count > 0, "Should detect compilation errors");
}
let (handle, stdin, stderr) = cmd!("jq", ".").no_echo().spawn_io_in_err().unwrap();
let input_handle = stdin.map(|mut stdin| {
thread::spawn(move || {
stdin.write_all(b"{\"name\": \"test\", \"age\": }").unwrap();
})
});
let error_handle = stderr.map(|stderr| {
thread::spawn(move || {
let reader = BufReader::new(stderr);
let mut has_parse_error = false;
#[allow(clippy::manual_flatten)]
for line in reader.lines() {
if let Ok(line) = line {
if line.contains("parse error") || line.contains("Invalid") {
has_parse_error = true;
}
}
}
has_parse_error
})
});
if let Some(h) = input_handle {
h.join().unwrap();
}
let _ = handle.wait();
if let Some(h) = error_handle {
let has_error = h.join().unwrap();
assert!(has_error, "Should detect JSON parse error");
}
}
#[test]
fn test_pattern_011_stdout_stderr_separation() {
let (handle, stdout, stderr) =
cmd!("sh", "-c", "echo 'normal output'; echo 'error message' >&2")
.no_echo()
.spawn_io_out_err()
.unwrap();
let stdout_handle = stdout.map(|stdout| {
thread::spawn(move || {
let mut buffer = Vec::new();
let mut reader = BufReader::new(stdout);
reader.read_to_end(&mut buffer).unwrap();
String::from_utf8_lossy(&buffer).trim().to_string()
})
});
let stderr_handle = stderr.map(|stderr| {
thread::spawn(move || {
let mut buffer = Vec::new();
let mut reader = BufReader::new(stderr);
reader.read_to_end(&mut buffer).unwrap();
String::from_utf8_lossy(&buffer).trim().to_string()
})
});
handle.wait().unwrap();
if let Some(h) = stdout_handle {
let stdout_result = h.join().unwrap();
assert_eq!(stdout_result, "normal output");
}
if let Some(h) = stderr_handle {
let stderr_result = h.join().unwrap();
assert_eq!(stderr_result, "error message");
}
}
#[test]
fn test_pattern_111_complete_io_control() {
let spawn = cmd!("sort").no_echo().spawn_io_all().unwrap();
let input_handle = spawn.stdin.map(|mut stdin| {
thread::spawn(move || {
stdin.write_all(b"zebra\napple\nbanana\n").unwrap();
})
});
let output_handle = spawn.stdout.map(|stdout| {
thread::spawn(move || {
let mut buffer = Vec::new();
let mut reader = BufReader::new(stdout);
reader.read_to_end(&mut buffer).unwrap();
String::from_utf8_lossy(&buffer).trim().to_string()
})
});
let error_handle = spawn.stderr.map(|stderr| {
thread::spawn(move || {
let mut buffer = Vec::new();
let mut reader = BufReader::new(stderr);
reader.read_to_end(&mut buffer).unwrap();
buffer.is_empty() })
});
if let Some(h) = input_handle {
h.join().unwrap();
}
spawn.handle.wait().unwrap();
if let Some(h) = output_handle {
let output = h.join().unwrap();
let lines: Vec<&str> = output.split('\n').collect();
assert_eq!(lines, vec!["apple", "banana", "zebra"]);
}
if let Some(h) = error_handle {
let no_errors = h.join().unwrap();
assert!(no_errors, "Sort command should not produce errors");
}
}
#[test]
fn test_classic_io_methods() {
let data = "apple\nbanana\ncherry\ndate\nfig";
let cursor = Cursor::new(data.as_bytes());
let output = cursor.pipe(cmd!("grep", "a")).no_echo().output().unwrap();
assert!(output.contains("apple"));
assert!(output.contains("banana"));
assert!(output.contains("date"));
let mut buffer = Vec::new();
cmd!("echo", "test_stream")
.no_echo()
.write_to(&mut buffer)
.unwrap();
let result = String::from_utf8(buffer).unwrap();
assert_eq!(result.trim(), "test_stream");
let input_data = "zebra\napple\nbanana\ncherry";
let input_reader = Cursor::new(input_data);
let mut output_buffer = Vec::new();
cmd!("sort")
.no_echo()
.run_with_io(input_reader, &mut output_buffer)
.unwrap();
let result = String::from_utf8(output_buffer).unwrap();
assert!(result.contains("apple"));
assert!(result.contains("banana"));
let invalid_rust_code = "fn main() { invalid syntax }";
let input_reader = Cursor::new(invalid_rust_code);
let mut error_buffer = Vec::new();
let _ = cmd!("rustc", "--error-format=short", "-")
.no_echo()
.run_with_err_io(input_reader, &mut error_buffer);
let error_output = String::from_utf8(error_buffer).unwrap();
assert!(!error_output.is_empty());
let input_data = "test data\nmore data";
let input_reader = Cursor::new(input_data);
let combined_buffer = Vec::new();
let cursor = Cursor::new(combined_buffer);
cmd!("sh", "-c", "cat; echo 'stderr message' >&2")
.no_echo()
.run_with_both_io(input_reader, cursor)
.unwrap();
}
#[test]
fn test_binary_data_patterns() {
let binary_data: Vec<u8> = vec![0x00, 0x01, 0x02, 0x03, 0xFF, 0xFE, 0xFD, 0xFC];
let (handle, stdin) = cmd!("wc", "-c").no_echo().spawn_io_in().unwrap();
let input_handle = stdin.map(|mut stdin| {
let data = binary_data.clone();
thread::spawn(move || {
stdin.write_all(&data).unwrap();
})
});
if let Some(h) = input_handle {
h.join().unwrap();
}
handle.wait().unwrap();
let (handle, stdin, stdout) = cmd!("cat").no_echo().spawn_io_in_out().unwrap();
let input_handle = stdin.map(|mut stdin| {
let data = binary_data.clone();
thread::spawn(move || {
stdin.write_all(&data).unwrap();
})
});
let output_handle = stdout.map(|stdout| {
thread::spawn(move || {
let mut buffer = Vec::new();
let mut reader = BufReader::new(stdout);
reader.read_to_end(&mut buffer).unwrap();
buffer
})
});
if let Some(h) = input_handle {
h.join().unwrap();
}
handle.wait().unwrap();
if let Some(h) = output_handle {
let output = h.join().unwrap();
assert_eq!(output, binary_data);
}
}
#[test]
fn test_error_handling_patterns() {
let result = cmd!("nonexistent_command_12345").no_echo().spawn_io_in();
assert!(result.is_err());
let result = cmd!("nonexistent_command_12345").no_echo().spawn_io_out();
assert!(result.is_err());
let result = cmd!("nonexistent_command_12345")
.no_echo()
.spawn_io_in_out();
assert!(result.is_err());
let result = cmd!("nonexistent_command_12345").no_echo().spawn_io_all();
assert!(result.is_err());
}
#[test]
fn test_pipeline_patterns() {
let (handle, stdout) = cmd!("echo", "hello")
.pipe(cmd!("tr", "a-z", "A-Z"))
.no_echo()
.spawn_io_out()
.unwrap();
let output_handle = stdout.map(|stdout| {
thread::spawn(move || {
let mut buffer = Vec::new();
let mut reader = BufReader::new(stdout);
reader.read_to_end(&mut buffer).unwrap();
String::from_utf8_lossy(&buffer).trim().to_string()
})
});
handle.wait().unwrap();
if let Some(h) = output_handle {
let output = h.join().unwrap();
assert_eq!(output, "HELLO");
}
let spawn = cmd!("cat")
.pipe(cmd!("sort"))
.no_echo()
.spawn_io_all()
.unwrap();
let input_handle = spawn.stdin.map(|mut stdin| {
thread::spawn(move || {
stdin.write_all(b"zebra\napple\nbanana\n").unwrap();
})
});
let output_handle = spawn.stdout.map(|stdout| {
thread::spawn(move || {
let mut buffer = Vec::new();
let mut reader = BufReader::new(stdout);
reader.read_to_end(&mut buffer).unwrap();
String::from_utf8_lossy(&buffer).trim().to_string()
})
});
if let Some(h) = input_handle {
h.join().unwrap();
}
spawn.handle.wait().unwrap();
if let Some(h) = output_handle {
let output = h.join().unwrap();
let lines: Vec<&str> = output.split('\n').collect();
assert_eq!(lines, vec!["apple", "banana", "zebra"]);
}
}
#[test]
fn test_pattern_performance() {
let large_data = "x".repeat(10000);
let (handle, stdin, stdout) = cmd!("wc", "-c").no_echo().spawn_io_in_out().unwrap();
let input_handle = stdin.map(|mut stdin| {
let data = large_data.clone();
thread::spawn(move || {
stdin.write_all(data.as_bytes()).unwrap();
})
});
let output_handle = stdout.map(|stdout| {
thread::spawn(move || {
let mut buffer = Vec::new();
let mut reader = BufReader::new(stdout);
reader.read_to_end(&mut buffer).unwrap();
String::from_utf8_lossy(&buffer).trim().to_string()
})
});
if let Some(h) = input_handle {
h.join().unwrap();
}
handle.wait().unwrap();
if let Some(h) = output_handle {
let output = h.join().unwrap();
let count: usize = output.parse().unwrap();
assert_eq!(count, 10000);
}
}