use super::*;
use std::process::Command;
#[test]
fn what_a_child_writes_to_both_streams_is_read_back_whole() {
let capture: Capture = Capture::new().expect("the files");
let (out, err) = capture.stdio().expect("the streams");
let status = Command::new("sh").args(["-c", "printf one; printf two >&2; printf three"]).stdout(out).stderr(err).status().expect("the child");
assert!(status.success());
let first: Captured = capture.snapshot(1024);
assert_eq!((first.stdout.as_slice(), first.stderr.as_slice()), (b"onethree".as_slice(), b"two".as_slice()));
assert!(first.notes.is_empty(), "{:?}", first.notes);
assert_eq!(capture.snapshot(1024), first, "reading does not consume");
}
#[test]
fn output_larger_than_a_read_buffer_is_collected_whole() {
let capture: Capture = Capture::new().expect("the files");
let (out, err) = capture.stdio().expect("the streams");
let status = Command::new("sh").args(["-c", "head -c 100000 /dev/zero | tr '\\0' x"]).stdout(out).stderr(err).status().expect("the child");
assert!(status.success());
let all: Captured = capture.snapshot(1_000_000);
assert_eq!(all.stdout.len(), 100_000);
assert!(all.notes.is_empty());
}
#[test]
fn output_past_the_limit_keeps_the_tail_and_says_what_was_left_out() {
let capture: Capture = Capture::new().expect("the files");
let (out, err) = capture.stdio().expect("the streams");
let status = Command::new("sh").args(["-c", "i=0; while [ $i -lt 2000 ]; do printf 'line %05d\\n' $i; i=$((i + 1)); done"]).stdout(out).stderr(err).status().expect("the child");
assert!(status.success());
let shown: Captured = capture.snapshot(100);
assert_eq!(shown.stdout.len(), 100);
assert!(String::from_utf8_lossy(shown.stdout.as_slice()).ends_with("line 01999\n"), "the tail is kept");
assert_eq!(shown.notes.len(), 1, "{:?}", shown.notes);
let written: usize = capture.snapshot(usize::MAX).stdout.len();
let left_out: String = format!("{} bytes at the start of the standard output", written - shown.stdout.len());
assert!(shown.notes[0].contains(left_out.as_str()) && shown.notes[0].contains("the last 100 bytes"), "{}", shown.notes[0]);
}
#[test]
fn the_snapshot_reads_the_length_it_found_and_no_more() {
let capture: Capture = Capture::new().expect("the files");
let (out, err) = capture.stdio().expect("the streams");
let found: usize = 100_000;
let status = Command::new("sh").args(["-c", format!("head --bytes={found} /dev/zero | tr '\\0' 'a'").as_str()]).stdout(out).stderr(err).status().expect("the child");
assert!(status.success());
let mut appender = capture.out.try_clone().expect("a second handle on the same file");
let mut chunks: usize = 0;
let (shown, notes) = capture.snapshot_stdout_with(usize::MAX, &mut || {
use std::io::{Seek, SeekFrom, Write};
chunks += 1;
assert!(chunks < 10_000, "the reader chased the growing end of the file");
appender.seek(SeekFrom::End(0)).expect("end");
appender.write_all(&[b'b'; 8192]).expect("append");
});
assert_eq!(shown.len(), found, "exactly the length found when the read began");
assert!(shown.iter().all(|byte| { return *byte == b'a'; }), "none of what was appended");
assert!(notes.is_empty(), "{notes:?}");
}
#[test]
fn a_read_that_fails_is_reported_and_not_taken_for_the_end() {
let directory: std::path::PathBuf = crate::test_support::fixture("output-capture-unreadable");
let path: std::path::PathBuf = directory.join("only-writable");
std::fs::write(&path, b"some output").expect("a file");
let write_only: File = std::fs::OpenOptions::new().write(true).open(&path).expect("open for writing only");
let capture: Capture = Capture { out: write_only.try_clone().expect("clone"), err: write_only };
let shown: Captured = capture.snapshot(1024);
assert!(shown.stdout.is_empty());
assert!(shown.notes.iter().any(|note| { return note.contains("could not be read whole"); }), "{:?}", shown.notes);
crate::test_support::remove(directory.as_path());
}