use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
struct Tree(PathBuf);
impl Tree {
fn new(name: &str) -> Self {
let path = Path::new(env!("CARGO_TARGET_TMPDIR")).join(name);
let _ = std::fs::remove_dir_all(&path);
std::fs::create_dir_all(&path).expect("the tree is created");
Self(path)
}
fn write(&self, name: &str, bytes: &[u8]) -> &Self {
let mut file = std::fs::File::create(self.0.join(name)).expect("the file is created");
file.write_all(bytes).expect("the file is written");
self
}
}
impl Drop for Tree {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.0);
}
}
struct Run {
stdout: String,
stderr: String,
code: Option<i32>,
}
fn run(tree: &Tree, arguments: &[&str]) -> Run {
let output = Command::new(env!("CARGO_BIN_EXE_dates-le"))
.arg(tree.0.as_os_str())
.arg("--no-ignore")
.args(arguments)
.output()
.expect("the binary runs");
Run {
stdout: String::from_utf8(output.stdout).expect("stdout is UTF-8"),
stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
code: output.status.code(),
}
}
const PNG: &[u8] = b"\x89PNG\r\n\x1a\n\0\0\0\rIHDR\0\0\x01\0\0\0\x01\0";
#[test]
fn a_binary_file_is_absent_from_the_report_and_counted_in_the_summary() {
let tree = Tree::new("binary-and-text");
tree.write("notes.md", b"shipped 2024-01-15\n")
.write("logo.png", PNG)
.write("icon.ico", PNG);
let run = run(&tree, &[]);
assert_eq!(run.stdout.lines().count(), 1, "{}", run.stdout);
assert!(run.stdout.contains("notes.md"), "{}", run.stdout);
assert!(!run.stdout.contains(".png"), "{}", run.stdout);
assert!(!run.stdout.contains(".ico"), "{}", run.stdout);
assert!(
run.stderr.contains("2 binary files skipped"),
"the summary must still say the walk covered less than the tree: {}",
run.stderr
);
assert_eq!(run.code, Some(0), "a date was found");
}
#[test]
fn strict_ignores_a_binary_file() {
let tree = Tree::new("binary-under-strict");
tree.write("notes.md", b"shipped 2024-01-15\n")
.write("logo.png", PNG);
assert_eq!(run(&tree, &["--strict"]).code, Some(0));
}
#[test]
fn a_text_file_that_cannot_be_read_is_named_and_fails_a_strict_run() {
let tree = Tree::new("undecodable-text");
tree.write("notes.md", b"shipped 2024-01-15\n")
.write("latin.log", b"caf\xe9 2024-01-15\n");
let plain = run(&tree, &[]);
assert_eq!(plain.stdout.lines().count(), 2, "{}", plain.stdout);
assert!(plain.stdout.contains("not UTF-8 text"), "{}", plain.stdout);
assert!(plain.stderr.contains("1 skipped"), "{}", plain.stderr);
assert!(
!plain.stderr.contains("binary"),
"undecodable text is not binary: {}",
plain.stderr
);
assert_eq!(
plain.code,
Some(0),
"a skipped file does not fail a plain run"
);
assert_eq!(run(&tree, &["--strict"]).code, Some(2));
}
#[test]
fn every_text_file_in_the_tree_is_read() {
let tree = Tree::new("many-file-types");
tree.write("settings.py", b"CUTOVER = \"2024-01-15\"\n")
.write("handler.go", b"const released = \"2024-01-15\"\n")
.write("Cargo.toml", b"cut = 2024-01-15\n")
.write("README.md", b"Released 2024-01-15.\n")
.write("Makefile", b"# updated 2024-01-15\n");
let run = run(&tree, &[]);
assert_eq!(run.stdout.lines().count(), 5, "{}", run.stdout);
assert!(
run.stderr.starts_with("5 dates in 5 files"),
"{}",
run.stderr
);
}