use crate::common::*;
use std::io::{Read, Write};
use std::path::PathBuf;
use std::process::{Command, Stdio};
#[test]
fn a_failing_test_leaves_no_scratch_behind() {
let seen = std::cell::RefCell::new(None::<PathBuf>);
let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let probe = tmp("probe");
std::fs::write(&probe, b"left behind?").unwrap();
*seen.borrow_mut() = Some(probe.to_path_buf());
panic!("a failing test, on purpose");
}));
assert!(r.is_err());
let probe = seen.into_inner().expect("the closure ran");
assert!(!probe.exists(), "{} survived a panic", probe.display());
}
#[test]
fn formats_now_reports_gzip() {
let out = Command::new(STUFFR).arg("formats").output().unwrap();
assert!(out.status.success());
let text = String::from_utf8(out.stdout).unwrap();
assert!(text.contains("gzip"), "gzip must appear: {text}");
assert!(
!text.contains("0 formats"),
"the build is no longer empty: {text}"
);
}
#[test]
fn info_names_the_format_and_the_rung() {
let src = tmp("info.txt");
let gz = tmp("info.txt.gz");
let _ = std::fs::remove_file(&gz);
std::fs::write(&src, b"payload").unwrap();
assert!(
Command::new(STUFFR)
.args(["pack", src.to_str().unwrap(), "--format", "gzip"])
.status()
.unwrap()
.success()
);
let out = Command::new(STUFFR)
.args(["info", gz.to_str().unwrap()])
.output()
.unwrap();
assert!(out.status.success());
let text = String::from_utf8(out.stdout).unwrap();
assert!(text.contains("gzip"), "{text}");
assert!(text.contains("exact"), "a file is read exactly: {text}");
}
#[test]
fn info_json_is_structured_and_names_how_the_format_was_detected() {
let src = tmp("json2.txt");
let gz = tmp("json2.txt.gz");
let _ = std::fs::remove_file(&gz);
std::fs::write(&src, b"payload").unwrap();
assert!(
Command::new(STUFFR)
.args(["pack", src.to_str().unwrap(), "--format", "gzip"])
.status()
.unwrap()
.success()
);
let out = Command::new(STUFFR)
.args(["info", "--json", gz.to_str().unwrap()])
.output()
.unwrap();
let text = String::from_utf8(out.stdout).unwrap();
let v: serde_json::Value =
serde_json::from_str(&text).unwrap_or_else(|e| panic!("not JSON: {e}\n{text}"));
assert_eq!(v["format"], "gzip");
assert_eq!(v["rung"], "exact");
assert_eq!(
v["detected_by"], "magic",
"a real gzip file is identified by its magic, not its name"
);
assert!(v["warnings"].as_array().unwrap().is_empty());
}
#[test]
fn info_reports_the_resolved_memory_limit() {
let src = tmp("info-mem.txt");
let gz = tmp("info-mem.txt.gz");
let _ = std::fs::remove_file(&gz);
std::fs::write(&src, b"payload").unwrap();
assert!(
Command::new(STUFFR)
.args(["pack", src.to_str().unwrap(), "--format", "gzip"])
.status()
.unwrap()
.success()
);
let out = Command::new(STUFFR)
.args(["info", "--memory-limit", "512M", gz.to_str().unwrap()])
.output()
.unwrap();
assert!(out.status.success());
let text = String::from_utf8(out.stdout).unwrap();
assert!(
text.contains("512 MiB"),
"an explicit --memory-limit must be reflected in the report: {text}"
);
let out = Command::new(STUFFR)
.args([
"info",
"--json",
"--memory-limit",
"512M",
gz.to_str().unwrap(),
])
.output()
.unwrap();
let text = String::from_utf8(out.stdout).unwrap();
let v: serde_json::Value =
serde_json::from_str(&text).unwrap_or_else(|e| panic!("not JSON: {e}\n{text}"));
assert_eq!(v["memory_limit"], 512 * 1024 * 1024);
}
#[test]
fn info_withholds_the_fidelity_claim_only_where_a_forward_read_could_lose_something() {
let dir = tmp("info-fidelity-matrix");
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
let one = dir.join("one.txt");
std::fs::write(&one, b"payload").unwrap();
let mut archives = Vec::new();
for (ext, has_trailing_index) in [("zip", true), ("tar", false), ("cpio", false), ("a", false)]
{
let path = dir.join(format!("bundle.{ext}"));
assert!(
Command::new(STUFFR)
.args(["pack", one.to_str().unwrap(), "-o", path.to_str().unwrap()])
.status()
.unwrap()
.success(),
"packing a .{ext} must succeed"
);
archives.push((ext, path, has_trailing_index));
}
for (ext, path, has_trailing_index) in &archives {
let bytes = std::fs::read(path).unwrap();
let text = String::from_utf8(
Command::new(STUFFR)
.args(["info", path.to_str().unwrap()])
.output()
.unwrap()
.stdout,
)
.unwrap();
assert!(text.contains("exact"), ".{ext} from a file: {text}");
assert!(
text.contains("nothing approximated"),
".{ext} from a FILE was read exactly — the claim is true and must be made: {text}"
);
let text = info_over_stdin(&bytes);
assert!(text.contains("forward-only"), ".{ext} on a pipe: {text}");
if *has_trailing_index {
assert!(
text.contains("not evaluated"),
".{ext} keeps its index at the END of the stream, so a forward read may well \
have approximated something and info has not looked: {text}"
);
assert!(!text.contains("nothing approximated"), ".{ext}: {text}");
} else {
assert!(
text.contains("nothing approximated"),
".{ext} carries every entry's metadata inline, so a forward read loses \
NOTHING — withholding the claim here is a false negative: {text}"
);
assert!(!text.contains("not evaluated"), ".{ext}: {text}");
}
let tested = |bytes: &[u8]| {
let mut child = Command::new(STUFFR)
.args(["test", "-"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
child.stdin.take().unwrap().write_all(bytes).unwrap();
child.wait_with_output().unwrap()
};
let out = tested(&bytes);
let stderr = String::from_utf8_lossy(&out.stderr);
if *has_trailing_index {
let cloaked = tested(&cloak_first_local_name(&bytes));
let cloaked_err = String::from_utf8_lossy(&cloaked.stderr);
assert!(
cloaked_err.contains("fidelity warning"),
".{ext} on a pipe must be able to really have warnings, or info would be \
right to claim none: {cloaked_err}"
);
assert!(
out.status.success() && !stderr.contains("fidelity warning"),
".{ext} on a pipe whose index agrees reports nothing since B2: {stderr}"
);
} else {
assert!(
out.status.success() && !stderr.contains("fidelity warning"),
".{ext} on a pipe must really have NO warnings, which is what makes info's \
claim true: {stderr}"
);
}
}
}
#[test]
fn info_on_a_piped_zip_does_not_claim_nothing_was_approximated() {
let dir = tmp("info-zip-fidelity");
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
let one = dir.join("one.txt");
std::fs::write(&one, b"payload").unwrap();
let zip = dir.join("bundle.zip");
assert!(
Command::new(STUFFR)
.args(["pack", one.to_str().unwrap(), "-o", zip.to_str().unwrap()])
.status()
.unwrap()
.success()
);
let bytes = std::fs::read(&zip).unwrap();
let text = info_over_stdin(&bytes);
assert!(
text.contains("forward-only"),
"the rung is still real and still reported: {text}"
);
assert!(
!text.contains("nothing approximated"),
"info has not opened the archive, so it must not claim this: {text}"
);
assert!(
text.contains("not evaluated"),
"the line must say the conclusion was withheld, not vanish: {text}"
);
let tested = |bytes: &[u8]| {
let mut child = Command::new(STUFFR)
.args(["test", "-"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
child.stdin.take().unwrap().write_all(bytes).unwrap();
child.wait_with_output().unwrap()
};
let cloaked = tested(&cloak_first_local_name(&bytes));
let stderr = String::from_utf8_lossy(&cloaked.stderr);
assert!(
stderr.contains("local header and central directory disagree on name"),
"`test` reads the archive to its index and must report the cloak: {stderr}"
);
assert_eq!(
info_over_stdin(&cloak_first_local_name(&bytes)),
text,
"info cannot tell the cloaked bytes from the clean ones"
);
let out = tested(&bytes);
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
out.status.success() && !stderr.contains("fidelity warning"),
"the clean bytes' index was read and agrees (ruling B2; two warnings in 0.22.0): \
{stderr}"
);
let out = Command::new(STUFFR)
.args(["info", zip.to_str().unwrap()])
.output()
.unwrap();
let text = String::from_utf8(out.stdout).unwrap();
assert!(text.contains("exact"), "a file is read exactly: {text}");
assert!(
text.contains("nothing approximated"),
"a seekable zip reads its central directory, so nothing is approximated: {text}"
);
}
#[test]
fn info_on_a_bare_codec_stream_still_reports_nothing_approximated() {
let src = tmp("info-codec-fidelity.txt");
let gz = tmp("info-codec-fidelity.txt.gz");
let _ = std::fs::remove_file(&gz);
std::fs::write(&src, b"payload").unwrap();
assert!(
Command::new(STUFFR)
.args(["pack", src.to_str().unwrap(), "--format", "gzip"])
.status()
.unwrap()
.success()
);
let bytes = std::fs::read(&gz).unwrap();
for (what, text) in [
(
"a file",
String::from_utf8(
Command::new(STUFFR)
.args(["info", gz.to_str().unwrap()])
.output()
.unwrap()
.stdout,
)
.unwrap(),
),
("a pipe", info_over_stdin(&bytes)),
] {
assert!(
text.contains("nothing approximated"),
"{what}: a bare codec stream has no container to open, so this claim is real \
and must survive: {text}"
);
assert!(!text.contains("not evaluated"), "{what}: {text}");
}
let out = Command::new(STUFFR)
.args(["info", "--json", gz.to_str().unwrap()])
.output()
.unwrap();
let v: serde_json::Value = serde_json::from_str(&String::from_utf8(out.stdout).unwrap())
.expect("info --json must be JSON");
assert_eq!(v["fidelity_evaluated"], true, "{v}");
}
#[test]
fn info_over_a_pipe_reports_forward_only_not_exact() {
let src = tmp("pipe-info.txt");
let gz = tmp("pipe-info.txt.gz");
let _ = std::fs::remove_file(&gz);
std::fs::write(&src, b"payload").unwrap();
assert!(
Command::new(STUFFR)
.args(["pack", src.to_str().unwrap(), "--format", "gzip"])
.status()
.unwrap()
.success()
);
let gz_bytes = std::fs::read(&gz).unwrap();
let mut child = Command::new(STUFFR)
.args(["info", "-"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.unwrap();
child.stdin.take().unwrap().write_all(&gz_bytes).unwrap();
let out = child.wait_with_output().unwrap();
assert!(out.status.success());
let text = String::from_utf8(out.stdout).unwrap();
assert!(text.contains("gzip"), "{text}");
assert!(
text.contains("forward-only"),
"a pipe cannot seek, so the rung must not be exact: {text}"
);
}
#[test]
fn cat_exits_cleanly_when_the_reader_closes_early() {
let src = tmp("broken-pipe.txt");
let gz = tmp("broken-pipe.txt.gz");
let _ = std::fs::remove_file(&gz);
let payload = "the quick brown fox jumps over the lazy dog\n".repeat(500_000);
std::fs::write(&src, payload.as_bytes()).unwrap();
assert!(
Command::new(STUFFR)
.args(["pack", src.to_str().unwrap(), "--format", "gzip"])
.status()
.unwrap()
.success()
);
let mut child = Command::new(STUFFR)
.args(["cat", gz.to_str().unwrap()])
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
let mut stdout = child.stdout.take().unwrap();
let mut prefix = [0u8; 16];
stdout
.read_exact(&mut prefix)
.expect("at least a few bytes before we stop reading");
drop(stdout);
let mut stderr = child.stderr.take().unwrap();
let mut err_text = String::new();
stderr.read_to_string(&mut err_text).unwrap();
let status = child.wait().unwrap();
assert!(
status.success(),
"an early-closing reader must not look like a failure: {status:?}"
);
assert!(
err_text.is_empty(),
"a clean broken-pipe exit prints nothing: {err_text}"
);
}
#[test]
fn formats_exits_cleanly_on_a_closed_stdout() {
let mut child = Command::new(STUFFR)
.arg("formats")
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
drop(child.stdout.take().unwrap());
let mut stderr = child.stderr.take().unwrap();
let mut err_text = String::new();
stderr.read_to_string(&mut err_text).unwrap();
let status = child.wait().unwrap();
assert!(
status.success(),
"an early-closing reader must not look like a failure: {status:?}"
);
assert!(
err_text.is_empty(),
"a clean broken-pipe exit prints nothing: {err_text}"
);
}
#[test]
fn pack_then_unpack_round_trips_through_the_binary() {
let src = tmp("e2e.txt");
let gz = tmp("e2e.txt.gz");
let back = tmp("e2e-back.txt");
let _ = std::fs::remove_file(&gz);
let _ = std::fs::remove_file(&back);
let plain = b"the quick brown fox ".repeat(2000);
std::fs::write(&src, &plain).unwrap();
assert!(
Command::new(STUFFR)
.args(["pack", src.to_str().unwrap()])
.status()
.unwrap()
.success()
);
assert!(
gz.exists(),
"pack must have written the .gz beside the input"
);
assert!(src.exists(), "pack must not destroy its input");
assert!(
Command::new(STUFFR)
.args(["unpack", gz.to_str().unwrap(), "-o", back.to_str().unwrap()])
.status()
.unwrap()
.success()
);
assert_eq!(
std::fs::read(&back).unwrap(),
plain,
"round trip must be byte-identical"
);
for p in [&src, &gz, &back] {
let _ = std::fs::remove_file(p);
}
}
#[test]
fn no_sync_is_accepted_by_pack_and_unpack_and_still_round_trips() {
let src = tmp("nosync-e2e.txt");
let gz = tmp("nosync-e2e.txt.gz");
let back = tmp("nosync-e2e-back.txt");
let _ = std::fs::remove_file(&gz);
let _ = std::fs::remove_file(&back);
let plain = b"the quick brown fox ".repeat(500);
std::fs::write(&src, &plain).unwrap();
assert!(
Command::new(STUFFR)
.args([
"pack",
src.to_str().unwrap(),
"--no-sync",
"--format",
"gzip"
])
.status()
.unwrap()
.success()
);
assert!(gz.exists(), "pack --no-sync must still write the output");
assert!(
Command::new(STUFFR)
.args([
"unpack",
gz.to_str().unwrap(),
"-o",
back.to_str().unwrap(),
"--no-sync",
])
.status()
.unwrap()
.success()
);
assert_eq!(
std::fs::read(&back).unwrap(),
plain,
"--no-sync must cost durability, never correctness"
);
for p in [&src, &gz, &back] {
let _ = std::fs::remove_file(p);
}
}
#[test]
fn cat_reads_a_stream_on_stdin() {
use std::io::Write;
use std::process::Stdio;
let src = tmp("pipe.txt");
let gz = tmp("pipe.txt.gz");
let _ = std::fs::remove_file(&gz);
std::fs::write(&src, b"needle in a haystack").unwrap();
assert!(
Command::new(STUFFR)
.args(["pack", src.to_str().unwrap(), "--format", "gzip"])
.status()
.unwrap()
.success()
);
let packed = std::fs::read(&gz).unwrap();
let mut child = Command::new(STUFFR)
.args(["cat", "-"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.unwrap();
child.stdin.as_mut().unwrap().write_all(&packed).unwrap();
let out = child.wait_with_output().unwrap();
assert!(out.status.success());
assert_eq!(out.stdout, b"needle in a haystack");
}
#[test]
fn pack_reads_input_from_stdin() {
let out = tmp("stdin-pack.gz");
let _ = std::fs::remove_file(&out);
let plain = b"stdin plaintext, packed then verified byte for byte".repeat(200);
let mut child = Command::new(STUFFR)
.args(["pack", "-", "-o", out.to_str().unwrap()])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
child.stdin.take().unwrap().write_all(&plain).unwrap();
let result = child.wait_with_output().unwrap();
assert!(
result.status.success(),
"stderr: {}",
String::from_utf8_lossy(&result.stderr)
);
assert!(out.exists(), "pack must have written the output file");
let cat_out = Command::new(STUFFR)
.args(["cat", out.to_str().unwrap()])
.output()
.unwrap();
assert!(cat_out.status.success());
assert_eq!(cat_out.stdout, plain, "round trip must be byte-identical");
}
#[test]
fn pack_writes_output_to_stdout() {
let src = tmp("stdout-pack.txt");
let _ = std::fs::remove_file(&src);
let plain = b"payload written straight through to stdout by pack".repeat(200);
std::fs::write(&src, &plain).unwrap();
let pack_out = Command::new(STUFFR)
.args(["pack", src.to_str().unwrap(), "--format", "gzip", "-o", "-"])
.output()
.unwrap();
assert!(
pack_out.status.success(),
"stderr: {}",
String::from_utf8_lossy(&pack_out.stderr)
);
assert_eq!(
&pack_out.stdout[..2],
&[0x1f, 0x8b],
"must be a real gzip stream"
);
let mut child = Command::new(STUFFR)
.args(["cat", "-"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.unwrap();
child
.stdin
.take()
.unwrap()
.write_all(&pack_out.stdout)
.unwrap();
let cat_out = child.wait_with_output().unwrap();
assert!(cat_out.status.success());
assert_eq!(
cat_out.stdout, plain,
"round trip through a piped stdout destination must be byte-identical"
);
}
#[test]
fn pack_over_a_pipe_says_no_fidelity_loss_not_a_read_rung() {
let out = tmp("pack-rung-pipe.gz");
let _ = std::fs::remove_file(&out);
let plain = b"payload".repeat(50);
let mut child = Command::new(STUFFR)
.args(["pack", "-", "-o", out.to_str().unwrap()])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
child.stdin.take().unwrap().write_all(&plain).unwrap();
let result = child.wait_with_output().unwrap();
assert!(result.status.success());
let err_text = String::from_utf8_lossy(&result.stderr);
assert!(err_text.contains("no fidelity loss"), "{err_text}");
assert!(
!err_text.contains("forward-only") && !err_text.contains("exact"),
"a write line makes no read-rung claim: {err_text}"
);
}
#[test]
fn pack_of_a_seekable_file_says_no_fidelity_loss() {
let src = tmp("pack-rung-file.txt");
let gz = tmp("pack-rung-file.txt.gz");
let _ = std::fs::remove_file(&gz);
std::fs::write(&src, b"payload").unwrap();
let out = Command::new(STUFFR)
.args(["pack", src.to_str().unwrap()])
.output()
.unwrap();
assert!(out.status.success());
let err_text = String::from_utf8_lossy(&out.stderr);
assert!(err_text.contains("no fidelity loss"), "{err_text}");
}
#[test]
fn an_existing_output_is_refused_with_exit_two() {
let src = tmp("clash.txt");
let gz = tmp("clash.txt.gz");
std::fs::write(&src, b"payload").unwrap();
std::fs::write(&gz, b"PRE-EXISTING").unwrap();
let out = Command::new(STUFFR)
.args(["pack", src.to_str().unwrap(), "--format", "gzip"])
.output()
.unwrap();
assert_eq!(out.status.code(), Some(2), "usage errors exit 2");
assert!(String::from_utf8_lossy(&out.stderr).contains("--force"));
assert_eq!(
std::fs::read(&gz).unwrap(),
b"PRE-EXISTING",
"the refused command changed nothing"
);
}
#[test]
fn a_bomb_exits_six_and_leaves_no_partial_output() {
let src = tmp("boom.bin");
let gz = tmp("boom.bin.gz");
let out_path = tmp("boom-out.bin");
let _ = std::fs::remove_file(&gz);
let _ = std::fs::remove_file(&out_path);
std::fs::write(&src, vec![0u8; 2 * 1024 * 1024]).unwrap();
assert!(
Command::new(STUFFR)
.args(["pack", src.to_str().unwrap(), "--format", "gzip"])
.status()
.unwrap()
.success()
);
let out = Command::new(STUFFR)
.args([
"unpack",
gz.to_str().unwrap(),
"-o",
out_path.to_str().unwrap(),
"--max-ratio",
"100",
])
.output()
.unwrap();
assert_eq!(out.status.code(), Some(6), "resource limits exit 6");
assert!(
!out_path.exists(),
"a refused decode must leave no partial file"
);
}
#[test]
fn an_unknown_verb_exits_two() {
let out = Command::new(STUFFR).arg("bogus").output().unwrap();
assert_eq!(out.status.code(), Some(2));
}
#[test]
fn examples_page_covers_every_format_and_flag() {
use clap::CommandFactory;
use stuffr_cli::cli::Cli;
let out = Command::new(STUFFR).arg("--examples").output().unwrap();
assert!(out.status.success(), "stuffr --examples must exit 0");
let text = String::from_utf8(out.stdout).unwrap();
for row in stuffr::registry().matrix() {
let id = row.id.as_str();
assert!(
text.contains(id),
"the examples page does not mention the `{id}` format"
);
}
let root = Cli::command();
for sub in root.get_subcommands() {
let name = sub.get_name();
if name == "help" {
continue;
}
assert!(
text.contains(name),
"the examples page does not mention the `{name}` subcommand"
);
for arg in sub.get_arguments() {
let Some(long) = arg.get_long() else {
continue;
};
if long == "help" || long == "version" {
continue;
}
let flag = format!("--{long}");
assert!(
text.contains(flag.as_str()),
"the examples page does not mention {flag}"
);
}
}
}
#[test]
fn the_examples_page_cannot_carry_a_stale_count_or_a_shipped_still_to_come() {
let out = Command::new(STUFFR).arg("--examples").output().unwrap();
assert!(out.status.success());
let text = String::from_utf8(out.stdout).unwrap();
let rows = stuffr::registry().matrix();
let containers: Vec<&str> = rows
.iter()
.filter(|r| stuffr::registry().container(r.id).is_some())
.map(|r| r.id.as_str())
.collect();
let codecs: Vec<&str> = rows
.iter()
.filter(|r| stuffr::registry().container(r.id).is_none())
.map(|r| r.id.as_str())
.collect();
const LEGACY_ONLY_CONTAINERS: &[&str] = &["lha", "arj", "arc", "zoo"];
let base_containers: Vec<&str> = containers
.iter()
.copied()
.filter(|id| !LEGACY_ONLY_CONTAINERS.contains(id))
.collect();
let full_container_count = containers.len();
for sentence in text.split(['.', '\n']) {
let lower = sentence.to_lowercase();
if !(lower.contains("still to come") || lower.contains("not yet supported")) {
continue;
}
for id in rows.iter().map(|r| r.id.as_str()) {
assert!(
!sentence.contains(id),
"the page says `{id}` is still to come, but this build registers it: \
{sentence:?}"
);
}
}
const NUMBERS: &[(&str, usize)] = &[
("one", 1),
("two", 2),
("three", 3),
("four", 4),
("five", 5),
("six", 6),
("seven", 7),
("eight", 8),
("nine", 9),
("ten", 10),
("eleven", 11),
("twelve", 12),
("thirteen", 13),
("fourteen", 14),
("fifteen", 15),
("sixteen", 16),
("seventeen", 17),
("eighteen", 18),
("nineteen", 19),
("twenty", 20),
];
let flat = text.split_whitespace().collect::<Vec<_>>().join(" ");
let words: Vec<&str> = flat.split(' ').collect();
for pair in words.windows(2) {
let (word, next) = (
pair[0].trim_matches(|c: char| !c.is_alphanumeric()),
pair[1],
);
let Some((_, value)) = NUMBERS.iter().find(|(w, _)| *w == word.to_lowercase()) else {
continue;
};
let noun = next
.trim_matches(|c: char| !c.is_alphanumeric())
.to_lowercase();
if noun == "containers" {
assert!(
*value == base_containers.len() || *value == full_container_count,
"the page says `{word} containers`, but this build has {} base containers \
({} once every legacy container is counted) — the counts on this page are \
what a user trusts before running `stuffr formats`",
base_containers.len(),
full_container_count
);
continue;
}
let expected = match noun.as_str() {
"formats" | "rows" => rows.len(),
"codecs" => codecs.len(),
_ => continue,
};
assert_eq!(
*value, expected,
"the page says `{word} {noun}`, but this build has {expected} — the counts \
on this page are what a user trusts before running `stuffr formats`"
);
}
let counting_sentences: Vec<&str> = flat
.split(". ")
.filter(|sentence| {
let l = sentence.to_lowercase();
NUMBERS
.iter()
.any(|(w, _)| l.contains(&format!("{w} containers")))
})
.collect();
assert!(
!counting_sentences.is_empty(),
"the page must state how many containers this build has"
);
let names_them_all = counting_sentences.iter().any(|sentence| {
containers.iter().all(|id| {
sentence
.split(|c: char| !c.is_alphanumeric())
.any(|word| word == *id)
})
});
assert!(
names_them_all,
"no sentence both counts the containers and names every one of them \
({containers:?}); the counting sentences are {counting_sentences:?}"
);
}
#[test]
fn examples_pages_first_worked_example_runs_as_documented() {
let dir = tmp("examples-first-example-dir");
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
let notes = dir.join("notes.txt");
let packed = dir.join("notes.txt.gz");
std::fs::write(¬es, b"the documented example's payload").unwrap();
let out = Command::new(STUFFR)
.arg("pack")
.arg("notes.txt")
.current_dir(&dir)
.output()
.unwrap();
assert!(
out.status.success(),
"stderr: {}",
String::from_utf8_lossy(&out.stderr)
);
assert!(
packed.exists(),
"the page says this writes notes.txt.gz beside it"
);
assert!(
notes.exists(),
"the page says the input, notes.txt, is left alone"
);
}
#[cfg(unix)]
#[test]
fn a_symlink_in_a_walked_tree_is_never_refused_and_the_page_may_not_say_it_is() {
let dir = tmp("examples-symlink-verdict");
let _ = std::fs::remove_dir_all(&dir);
let tree = dir.join("tree");
std::fs::create_dir_all(&tree).unwrap();
std::fs::write(tree.join("file.txt"), b"payload").unwrap();
std::os::unix::fs::symlink("file.txt", tree.join("link.txt")).unwrap();
let registry = stuffr::registry();
let mut measured = 0usize;
for row in registry.matrix() {
if registry.container(row.id).is_none() || !row.write {
continue;
}
let id = row.id.as_str();
let out_path = dir.join(format!("out-{id}"));
for strict in [false, true] {
let _ = std::fs::remove_file(&out_path);
let mut cmd = Command::new(STUFFR);
cmd.arg("pack")
.arg(&tree)
.arg("-o")
.arg(&out_path)
.arg("--format")
.arg(id);
if strict {
cmd.arg("--strict-fidelity");
}
let out = cmd.output().unwrap();
let code = out.status.code();
assert!(
matches!(code, Some(0) | Some(4)),
"`pack` over a tree containing a symlink answered exit {code:?} for `{id}` \
(strict-fidelity: {strict}). A kind a container cannot store is a fidelity \
LOSS, never a refusal — and exit 3 in particular is what `examples.txt` \
wrongly promised for `arj` until Phase 3c's final review measured it. \
stderr: {}",
String::from_utf8_lossy(&out.stderr)
);
}
measured += 1;
}
assert!(
measured >= 6,
"only {measured} writable containers were exercised; this build registers six, so the \
registry walk has stopped finding them and the measurement proves nothing"
);
let _ = std::fs::remove_dir_all(&dir);
let out = Command::new(STUFFR).arg("--examples").output().unwrap();
assert!(out.status.success(), "stuffr --examples must exit 0");
let text = String::from_utf8(out.stdout).unwrap();
let flat = text.split_whitespace().collect::<Vec<_>>().join(" ");
for sentence in flat.split(". ") {
let lower = sentence.to_lowercase();
if !lower.contains("symlink") {
continue;
}
if !(lower.contains("refused") || lower.contains("refuses")) {
continue;
}
assert!(
!lower.contains("exit 3"),
"the examples page says a symlink is refused at exit 3, which the measurement \
above proves no writable container does: {sentence:?}"
);
}
}
#[test]
fn pack_refuses_a_read_only_legacy_format_clearly() {
let registry = stuffr::registry();
let dir = tmp("legacy-format-refusal-dir");
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
let src = dir.join("notes.txt");
std::fs::write(&src, b"payload").unwrap();
for (name, out_ext) in [("arc", "arc"), ("zoo", "zoo")] {
let registered = registry.container(stuffr::FormatId::new(name)).is_some()
|| registry.codec(stuffr::FormatId::new(name)).is_some();
if !registered {
continue;
}
let dst = dir.join(format!("out.{out_ext}"));
let res = Command::new(STUFFR)
.args([
"pack",
src.to_str().unwrap(),
"--format",
name,
"-o",
dst.to_str().unwrap(),
])
.output()
.unwrap();
let err = String::from_utf8_lossy(&res.stderr);
assert_eq!(
res.status.code(),
Some(3),
"`--format {name}` must refuse at exit 3 (this build cannot do that), got \
{:?}; stderr: {err}",
res.status.code()
);
let lower = err.to_lowercase();
assert!(
lower.contains(&format!(
"`{name}` can be read but not written by this build"
)),
"`--format {name}`'s refusal must be the one shared capability sentence, \
naming the format, rather than a per-format phrasing: {err}"
);
assert!(
!dst.exists(),
"a refused pack must not leave a partial `{}` behind",
dst.display()
);
}
}
#[test]
fn pack_writes_a_compress_stream_the_system_tool_reads() {
let uncompress_bin = require_bin("uncompress");
let dir = tmp("compress-write-dir");
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
let src = dir.join("notes.txt");
let plain = b"the quick brown fox jumps over the lazy dog\n".repeat(200);
std::fs::write(&src, &plain).unwrap();
let dst = dir.join("notes.txt.Z");
let res = Command::new(STUFFR)
.args([
"pack",
src.to_str().unwrap(),
"--format",
"compress",
"-o",
dst.to_str().unwrap(),
])
.output()
.unwrap();
assert!(
res.status.success(),
"pack --format compress must now succeed: {}",
String::from_utf8_lossy(&res.stderr)
);
assert!(dst.exists(), "pack must have written {}", dst.display());
let out = Command::new(&uncompress_bin)
.arg("-c")
.arg(&dst)
.output()
.unwrap();
assert!(
out.status.success(),
"the system uncompress must read this build's own .Z output: {}",
String::from_utf8_lossy(&out.stderr)
);
assert_eq!(
out.stdout, plain,
"the system uncompress's output must match the original plaintext exactly"
);
let cat = Command::new(STUFFR)
.args(["cat", dst.to_str().unwrap()])
.output()
.unwrap();
assert!(cat.status.success());
assert_eq!(cat.stdout, plain);
}
#[test]
fn pack_writes_an_lha_archive_the_system_tool_reads() {
let lha_bin = require_lhasa();
let banner = lhasa_banner(&lha_bin);
let dir = tmp("lha-write-dir");
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(dir.join("tree/sub")).unwrap();
let alpha = b"the quick brown fox jumps over the lazy dog\n".repeat(120);
std::fs::write(dir.join("tree/alpha.txt"), &alpha).unwrap();
std::fs::write(dir.join("tree/sub/beta.bin"), b"beta\n").unwrap();
let dst = dir.join("out.lzh");
let res = Command::new(STUFFR)
.args([
"pack",
dir.join("tree").to_str().unwrap(),
"--format",
"lha",
"-o",
dst.to_str().unwrap(),
])
.output()
.unwrap();
assert!(
res.status.success(),
"pack --format lha must now succeed: {}",
String::from_utf8_lossy(&res.stderr)
);
assert!(dst.exists(), "pack must have written {}", dst.display());
let test = Command::new(&lha_bin).arg("t").arg(&dst).output().unwrap();
assert!(
test.status.success(),
"{banner} must accept an archive stuffr wrote: {}\n{}",
String::from_utf8_lossy(&test.stdout),
String::from_utf8_lossy(&test.stderr)
);
let out = dir.join("out");
std::fs::create_dir_all(&out).unwrap();
let extract = Command::new(&lha_bin)
.arg(format!("xfw={}", out.display()))
.arg(&dst)
.output()
.unwrap();
assert!(
extract.status.success(),
"{banner} must extract it: {}",
String::from_utf8_lossy(&extract.stderr)
);
assert_eq!(std::fs::read(out.join("tree/alpha.txt")).unwrap(), alpha);
assert_eq!(
std::fs::read(out.join("tree/sub/beta.bin")).unwrap(),
b"beta\n"
);
let list = Command::new(STUFFR)
.args(["list", dst.to_str().unwrap()])
.output()
.unwrap();
assert!(list.status.success());
let listing = String::from_utf8_lossy(&list.stdout);
for name in ["tree/alpha.txt", "tree/sub/beta.bin"] {
assert!(
listing.contains(name),
"`stuffr list` lost {name}: {listing}"
);
}
}
#[test]
fn pack_writes_an_arj_archive_stuffr_reads_back() {
let dir = tmp("arj-write-dir");
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(dir.join("tree/sub")).unwrap();
let alpha = b"the quick brown fox jumps over the lazy dog\n".repeat(120);
std::fs::write(dir.join("tree/alpha.txt"), &alpha).unwrap();
std::fs::write(dir.join("tree/sub/beta.bin"), b"beta\n").unwrap();
let dst = dir.join("out.arj");
let res = Command::new(STUFFR)
.args([
"pack",
dir.join("tree").to_str().unwrap(),
"--format",
"arj",
"-o",
dst.to_str().unwrap(),
])
.output()
.unwrap();
assert!(
res.status.success(),
"pack --format arj must now succeed: {}",
String::from_utf8_lossy(&res.stderr)
);
assert!(dst.exists(), "pack must have written {}", dst.display());
let head = std::fs::read(&dst).unwrap();
assert_eq!(
&head[0..2],
&[0x60, 0xEA],
"an ARJ archive opens with its own header id"
);
let verify = Command::new(STUFFR)
.args(["test", dst.to_str().unwrap()])
.output()
.unwrap();
assert!(
verify.status.success(),
"`stuffr test` must accept an archive stuffr wrote: {}",
String::from_utf8_lossy(&verify.stderr)
);
let list = Command::new(STUFFR)
.args(["list", dst.to_str().unwrap()])
.output()
.unwrap();
assert!(list.status.success());
let listing = String::from_utf8_lossy(&list.stdout);
for name in ["tree/alpha.txt", "tree/sub/beta.bin"] {
assert!(
listing.contains(name),
"`stuffr list` lost {name}: {listing}"
);
}
let out = dir.join("out");
let unpack = Command::new(STUFFR)
.args(["unpack", dst.to_str().unwrap(), "-C", out.to_str().unwrap()])
.output()
.unwrap();
assert!(
unpack.status.success(),
"unpack must restore the tree: {}",
String::from_utf8_lossy(&unpack.stderr)
);
assert_eq!(std::fs::read(out.join("tree/alpha.txt")).unwrap(), alpha);
assert_eq!(
std::fs::read(out.join("tree/sub/beta.bin")).unwrap(),
b"beta\n"
);
assert!(
out.join("tree/sub").is_dir(),
"the directory entries must come back as directories, which is what \
ContainerCaps::stores_dirs claims"
);
}
#[test]
fn a_corrupt_archive_exits_five_not_one() {
let src = tmp("cli-corrupt.txt");
let gz = tmp("cli-corrupt.txt.gz");
let out = tmp("cli-corrupt-out.txt");
let _ = std::fs::remove_file(&gz);
let _ = std::fs::remove_file(&out);
std::fs::write(&src, b"the quick brown fox ".repeat(200)).unwrap();
assert!(
Command::new(STUFFR)
.args(["pack", src.to_str().unwrap(), "--format", "gzip"])
.status()
.unwrap()
.success()
);
let mut bytes = std::fs::read(&gz).unwrap();
let mid = bytes.len() / 2;
bytes[mid] ^= 0xFF;
std::fs::write(&gz, &bytes).unwrap();
let res = Command::new(STUFFR)
.args(["unpack", gz.to_str().unwrap(), "-o", out.to_str().unwrap()])
.output()
.unwrap();
assert_eq!(
res.status.code(),
Some(5),
"a corrupt archive must be distinguishable from a full disk"
);
}
#[test]
fn unpack_accepts_an_explicit_format() {
let src = tmp("fmt-unpack.txt");
let gz = tmp("fmt-unpack.txt.gz");
let out = tmp("fmt-unpack-out.txt");
let _ = std::fs::remove_file(&gz);
let _ = std::fs::remove_file(&out);
std::fs::write(&src, b"payload").unwrap();
assert!(
Command::new(STUFFR)
.args(["pack", src.to_str().unwrap(), "--format", "gzip"])
.status()
.unwrap()
.success()
);
let res = Command::new(STUFFR)
.args([
"unpack",
gz.to_str().unwrap(),
"--format",
"gzip",
"-o",
out.to_str().unwrap(),
])
.output()
.unwrap();
assert!(
res.status.success(),
"stderr: {}",
String::from_utf8_lossy(&res.stderr)
);
assert_eq!(std::fs::read(&out).unwrap(), b"payload");
for p in [&src, &gz, &out] {
let _ = std::fs::remove_file(p);
}
}
#[test]
fn cat_accepts_an_explicit_format() {
let src = tmp("fmt-cat.txt");
let gz = tmp("fmt-cat.txt.gz");
let _ = std::fs::remove_file(&gz);
std::fs::write(&src, b"payload").unwrap();
assert!(
Command::new(STUFFR)
.args(["pack", src.to_str().unwrap(), "--format", "gzip"])
.status()
.unwrap()
.success()
);
let res = Command::new(STUFFR)
.args(["cat", gz.to_str().unwrap(), "--format", "gzip"])
.output()
.unwrap();
assert!(
res.status.success(),
"stderr: {}",
String::from_utf8_lossy(&res.stderr)
);
assert_eq!(res.stdout, b"payload");
}
#[test]
fn pack_with_no_flags_defaults_to_gzip() {
let src = tmp("bare-default.txt");
let gz = tmp("bare-default.txt.gz");
let _ = std::fs::remove_file(&gz);
std::fs::write(&src, b"payload").unwrap();
let out = Command::new(STUFFR)
.args(["pack", src.to_str().unwrap()])
.output()
.unwrap();
assert!(
out.status.success(),
"stderr: {}",
String::from_utf8_lossy(&out.stderr)
);
assert!(
gz.exists(),
"a bare pack with no --format must default to gzip and write {}.gz",
src.display()
);
}
#[test]
fn unpack_format_flag_is_honoured_not_merely_accepted() {
let src = tmp("fmt-deflate.txt");
let packed = tmp("fmt-deflate.dat");
let out = tmp("fmt-deflate-out.txt");
for p in [&packed, &out] {
let _ = std::fs::remove_file(p);
}
let payload = b"the quick brown fox jumps over the lazy dog".repeat(50);
std::fs::write(&src, &payload).unwrap();
assert!(
Command::new(STUFFR)
.args([
"pack",
src.to_str().unwrap(),
"--format",
"deflate",
"-o",
packed.to_str().unwrap(),
])
.status()
.unwrap()
.success()
);
let no_format = Command::new(STUFFR)
.args([
"unpack",
packed.to_str().unwrap(),
"-o",
out.to_str().unwrap(),
"--force",
])
.output()
.unwrap();
assert!(
!no_format.status.success(),
"a raw deflate stream must NOT be detectable without --format"
);
let with_format = Command::new(STUFFR)
.args([
"unpack",
packed.to_str().unwrap(),
"--format",
"deflate",
"-o",
out.to_str().unwrap(),
"--force",
])
.output()
.unwrap();
assert!(
with_format.status.success(),
"stderr: {}",
String::from_utf8_lossy(&with_format.stderr)
);
assert_eq!(std::fs::read(&out).unwrap(), payload);
for p in [&src, &packed, &out] {
let _ = std::fs::remove_file(p);
}
}
#[test]
fn cat_format_flag_is_honoured_not_merely_accepted() {
let src = tmp("fmt-cat-deflate.txt");
let packed = tmp("fmt-cat-deflate.dat");
let _ = std::fs::remove_file(&packed);
let payload = b"the quick brown fox jumps over the lazy dog".repeat(50);
std::fs::write(&src, &payload).unwrap();
assert!(
Command::new(STUFFR)
.args([
"pack",
src.to_str().unwrap(),
"--format",
"deflate",
"-o",
packed.to_str().unwrap(),
])
.status()
.unwrap()
.success()
);
let no_format = Command::new(STUFFR)
.args(["cat", packed.to_str().unwrap()])
.output()
.unwrap();
assert!(
!no_format.status.success(),
"a raw deflate stream must NOT be detectable without --format"
);
let with_format = Command::new(STUFFR)
.args(["cat", packed.to_str().unwrap(), "--format", "deflate"])
.output()
.unwrap();
assert!(
with_format.status.success(),
"stderr: {}",
String::from_utf8_lossy(&with_format.stderr)
);
assert_eq!(with_format.stdout, payload);
for p in [&src, &packed] {
let _ = std::fs::remove_file(p);
}
}
#[test]
fn an_unknown_format_name_is_a_usage_error_on_every_verb() {
for verb in ["unpack", "cat"] {
let out = Command::new(STUFFR)
.args([verb, "/dev/null", "--format", "bogus"])
.output()
.unwrap();
assert_eq!(
out.status.code(),
Some(2),
"{verb} --format bogus must be a usage error"
);
assert!(
String::from_utf8_lossy(&out.stderr).contains("gzip"),
"the error must name what this build does have"
);
}
}
#[test]
fn cat_honours_max_ratio() {
let src = tmp("cat-bomb.bin");
let gz = tmp("cat-bomb.bin.gz");
let _ = std::fs::remove_file(&gz);
std::fs::write(&src, vec![0u8; 2 * 1024 * 1024]).unwrap();
assert!(
Command::new(STUFFR)
.args(["pack", src.to_str().unwrap(), "--format", "gzip"])
.status()
.unwrap()
.success()
);
let out = Command::new(STUFFR)
.args(["cat", gz.to_str().unwrap(), "--max-ratio", "100"])
.output()
.unwrap();
assert_eq!(out.status.code(), Some(6), "resource limits exit 6");
}
#[test]
fn formats_lists_every_registered_format() {
let out = Command::new(STUFFR).arg("formats").output().unwrap();
let text = String::from_utf8(out.stdout).unwrap();
for row in stuffr::registry().matrix() {
assert!(
text.contains(row.id.as_str()),
"`stuffr formats` omits {}: {text}",
row.id
);
}
}
#[test]
fn the_formats_note_sits_below_the_table_after_a_blank_line() {
let out = Command::new(STUFFR).arg("formats").output().unwrap();
let text = String::from_utf8(out.stdout).unwrap();
let lines: Vec<&str> = text.lines().collect();
let note = lines
.iter()
.position(|l| l.starts_with("(WRITE shows"))
.unwrap_or_else(|| panic!("`stuffr formats` printed no WRITE note:\n{text}"));
for row in stuffr::registry().matrix() {
let at = lines
.iter()
.position(|l| l.starts_with(row.id.as_str()))
.unwrap_or_else(|| panic!("`stuffr formats` omits {}:\n{text}", row.id));
assert!(
at < note,
"{} is listed at line {at}, below the note at line {note}:\n{text}",
row.id
);
}
assert!(
note > 0,
"the note is the first line, so no table precedes it"
);
assert_eq!(
lines[note - 1],
"",
"the note must be separated from the table by a blank line, got {:?}:\n{text}",
lines[note - 1]
);
}
#[test]
fn a_malformed_memory_limit_is_a_usage_error() {
let src = tmp("mem-bad.txt");
let out_path = tmp("mem-bad.gz");
let _ = std::fs::remove_file(&out_path);
std::fs::write(&src, b"payload").unwrap();
let out = Command::new(STUFFR)
.args(["pack", "--memory-limit", "512MB", "--format", "gzip", "-o"])
.arg(&out_path)
.arg(&src)
.output()
.unwrap();
assert_eq!(out.status.code(), Some(2), "malformed size must be exit 2");
let err = String::from_utf8_lossy(&out.stderr);
assert!(
err.contains("512M") && err.contains("2G"),
"must name the accepted forms concretely: {err}"
);
assert!(!out_path.exists(), "a refused pack must leave no output");
}
#[test]
fn threads_is_not_offered_on_decode_subcommands() {
for sub in ["unpack", "cat"] {
let out = Command::new(STUFFR)
.args([sub, "--threads", "4", "-"])
.output()
.unwrap();
let err = String::from_utf8_lossy(&out.stderr);
assert!(
err.contains("unexpected argument") || err.contains("--threads"),
"{sub} must reject --threads, got: {err}"
);
}
}
#[test]
fn the_binary_reports_its_version() {
let want = env!("CARGO_PKG_VERSION");
let out = Command::new(STUFFR).arg("--version").output().unwrap();
let s = String::from_utf8_lossy(&out.stdout);
assert!(s.contains(want), "--version must report {want}, got: {s}");
}