use std::process::Command;
use tempfile::NamedTempFile;
const FSTOOL: &str = env!("CARGO_BIN_EXE_fstool");
fn which(tool: &str) -> bool {
Command::new("sh")
.arg("-c")
.arg(format!("command -v {tool}"))
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
#[test]
fn cli_build_ls_cat_add_roundtrip() {
if !which("e2fsck") {
eprintln!("skipping: e2fsck not installed");
return;
}
let srcdir = tempfile::tempdir().unwrap();
std::fs::write(srcdir.path().join("one.txt"), b"first\n").unwrap();
let spec = NamedTempFile::new().unwrap();
std::fs::write(
spec.path(),
format!(
"[filesystem]\ntype = \"ext4\"\nsource = \"{}\"\nblock_size = 1024\n",
srcdir.path().display()
),
)
.unwrap();
let img = NamedTempFile::new().unwrap();
let out = Command::new(FSTOOL)
.arg("build")
.arg(spec.path())
.arg("-o")
.arg(img.path())
.output()
.unwrap();
assert!(
out.status.success(),
"build failed: {}",
String::from_utf8_lossy(&out.stderr)
);
let out = Command::new(FSTOOL)
.args(["ls"])
.arg(img.path())
.arg("/")
.output()
.unwrap();
assert!(out.status.success());
let listing = String::from_utf8_lossy(&out.stdout);
assert!(
listing.contains("one.txt"),
"ls missing one.txt:\n{listing}"
);
let out = Command::new(FSTOOL)
.args(["cat"])
.arg(img.path())
.arg("/one.txt")
.output()
.unwrap();
assert!(out.status.success());
assert_eq!(out.stdout, b"first\n");
let extra = NamedTempFile::new().unwrap();
std::fs::write(extra.path(), b"added via cli\n").unwrap();
let out = Command::new(FSTOOL)
.arg("add")
.arg(img.path())
.arg(extra.path())
.arg("/two.txt")
.output()
.unwrap();
assert!(
out.status.success(),
"add failed: {}",
String::from_utf8_lossy(&out.stderr)
);
let fsck = Command::new("e2fsck")
.arg("-fn")
.arg(img.path())
.output()
.unwrap();
assert!(
fsck.status.success(),
"e2fsck failed after add:\n{}",
String::from_utf8_lossy(&fsck.stdout)
);
let out = Command::new(FSTOOL)
.args(["cat"])
.arg(img.path())
.arg("/two.txt")
.output()
.unwrap();
assert!(out.status.success());
assert_eq!(out.stdout, b"added via cli\n");
}
#[test]
fn cli_rm_file_and_empty_dir() {
if !which("e2fsck") {
eprintln!("skipping: e2fsck not installed");
return;
}
let srcdir = tempfile::tempdir().unwrap();
std::fs::write(srcdir.path().join("doomed.txt"), b"bye\n").unwrap();
std::fs::create_dir(srcdir.path().join("emptydir")).unwrap();
std::fs::create_dir(srcdir.path().join("fulldir")).unwrap();
std::fs::write(srcdir.path().join("fulldir/keep"), b"k\n").unwrap();
let img = NamedTempFile::new().unwrap();
let out = Command::new(FSTOOL)
.args(["ext-build", "--kind", "ext4"])
.arg(srcdir.path())
.arg("-o")
.arg(img.path())
.output()
.unwrap();
assert!(out.status.success());
let out = Command::new(FSTOOL)
.arg("rm")
.arg(img.path())
.arg("/doomed.txt")
.output()
.unwrap();
assert!(
out.status.success(),
"rm file failed: {}",
String::from_utf8_lossy(&out.stderr)
);
let out = Command::new(FSTOOL)
.arg("rm")
.arg(img.path())
.arg("/emptydir")
.output()
.unwrap();
assert!(
out.status.success(),
"rm empty dir failed: {}",
String::from_utf8_lossy(&out.stderr)
);
let out = Command::new(FSTOOL)
.arg("rm")
.arg(img.path())
.arg("/fulldir")
.output()
.unwrap();
assert!(!out.status.success(), "rm non-empty dir should have failed");
let fsck = Command::new("e2fsck")
.arg("-fn")
.arg(img.path())
.output()
.unwrap();
assert!(
fsck.status.success(),
"e2fsck failed after rm:\n{}",
String::from_utf8_lossy(&fsck.stdout)
);
let out = Command::new(FSTOOL)
.args(["ls"])
.arg(img.path())
.arg("/")
.output()
.unwrap();
let listing = String::from_utf8_lossy(&out.stdout);
assert!(!listing.contains("doomed.txt"), "doomed.txt still present");
assert!(!listing.contains("emptydir"), "emptydir still present");
assert!(listing.contains("fulldir"), "fulldir wrongly removed");
}
#[test]
fn cli_info_reports_ext4() {
let srcdir = tempfile::tempdir().unwrap();
std::fs::write(srcdir.path().join("x"), b"y\n").unwrap();
let img = NamedTempFile::new().unwrap();
let out = Command::new(FSTOOL)
.args(["ext-build", "--kind", "ext4"])
.arg(srcdir.path())
.arg("-o")
.arg(img.path())
.output()
.unwrap();
assert!(out.status.success());
let out = Command::new(FSTOOL)
.arg("info")
.arg(img.path())
.output()
.unwrap();
assert!(out.status.success());
let info = String::from_utf8_lossy(&out.stdout);
assert!(info.contains("Ext4"), "info missing kind:\n{info}");
assert!(
info.contains("block size"),
"info missing block size:\n{info}"
);
}