112 lines
4.2 KiB
Rust
112 lines
4.2 KiB
Rust
//! The robustness harness visits each directory once, including symlink aliases and cycles.
|
|
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::{Command, Stdio};
|
|
use std::time::{Duration, Instant};
|
|
|
|
const BIN: &str = env!("CARGO_BIN_EXE_pdfcraft-cli");
|
|
|
|
struct Workdir(PathBuf);
|
|
|
|
impl Workdir {
|
|
fn new(test: &str) -> Self {
|
|
let dir = std::env::temp_dir().join(format!("pdfcraft-cli-check-{test}-{}", std::process::id()));
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
std::fs::create_dir_all(dir.join("input/nested")).unwrap();
|
|
let objects = [
|
|
"<< /Type /Catalog /Pages 2 0 R >>",
|
|
"<< /Type /Pages /Kids [3 0 R] /Count 1 /MediaBox [0 0 20 30] >>",
|
|
"<< /Type /Page /Parent 2 0 R >>",
|
|
];
|
|
let mut bytes = b"%PDF-1.7\n".to_vec();
|
|
let mut offsets = Vec::new();
|
|
for (i, object) in objects.iter().enumerate() {
|
|
offsets.push(bytes.len());
|
|
bytes.extend_from_slice(format!("{} 0 obj\n{object}\nendobj\n", i + 1).as_bytes());
|
|
}
|
|
let xref = bytes.len();
|
|
bytes.extend_from_slice(b"xref\n0 4\n0000000000 65535 f \n");
|
|
for offset in offsets {
|
|
bytes.extend_from_slice(format!("{offset:010} 00000 n \n").as_bytes());
|
|
}
|
|
bytes.extend_from_slice(format!("trailer\n<< /Size 4 /Root 1 0 R >>\nstartxref\n{xref}\n%%EOF\n").as_bytes());
|
|
std::fs::write(dir.join("input/a.pdf"), &bytes).unwrap();
|
|
std::fs::write(dir.join("input/nested/b.PDF"), &bytes).unwrap();
|
|
Self(dir)
|
|
}
|
|
|
|
fn check(&self, inputs: &[&Path]) -> Vec<serde_json::Value> {
|
|
let report = self.0.join("report.json");
|
|
let mut child = Command::new(BIN)
|
|
.arg("check")
|
|
.args(inputs)
|
|
.args(["--dpi", "36", "--timeout", "2", "--json"])
|
|
.arg(&report)
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped())
|
|
.spawn()
|
|
.unwrap();
|
|
let deadline = Instant::now() + Duration::from_secs(10);
|
|
while child.try_wait().unwrap().is_none() {
|
|
if Instant::now() >= deadline {
|
|
child.kill().unwrap();
|
|
let output = child.wait_with_output().unwrap();
|
|
panic!("check did not finish scanning directories: {}", String::from_utf8_lossy(&output.stderr));
|
|
}
|
|
std::thread::sleep(Duration::from_millis(10));
|
|
}
|
|
let output = child.wait_with_output().unwrap();
|
|
assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr));
|
|
serde_json::from_slice(&std::fs::read(report).unwrap()).unwrap()
|
|
}
|
|
}
|
|
|
|
impl Drop for Workdir {
|
|
fn drop(&mut self) {
|
|
let _ = std::fs::remove_dir_all(&self.0);
|
|
}
|
|
}
|
|
|
|
fn assert_two_valid_pdfs(results: &[serde_json::Value]) {
|
|
assert_eq!(results.len(), 2, "each PDF should be checked once: {results:?}");
|
|
for result in results {
|
|
assert_eq!(result["status"], "ok", "{result}");
|
|
assert_eq!(result["pages"], 1, "{result}");
|
|
}
|
|
assert!(results[0]["file"].as_str().unwrap().ends_with("a.pdf"));
|
|
assert!(results[1]["file"].as_str().unwrap().ends_with("b.PDF"));
|
|
}
|
|
|
|
#[test]
|
|
fn check_scans_overlapping_directories_once() {
|
|
let dir = Workdir::new("overlap");
|
|
let input = dir.0.join("input");
|
|
assert_two_valid_pdfs(&dir.check(&[&input, &input, &input.join("nested")]));
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
#[test]
|
|
fn check_scans_a_directory_symlink_cycle_once() {
|
|
let dir = Workdir::new("cycle");
|
|
let input = dir.0.join("input");
|
|
std::os::unix::fs::symlink(".", input.join("again")).unwrap();
|
|
assert_two_valid_pdfs(&dir.check(&[&input]));
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
#[test]
|
|
fn check_follows_directory_and_file_symlinks() {
|
|
let dir = Workdir::new("aliases");
|
|
let input = dir.0.join("input");
|
|
let alias = dir.0.join("alias");
|
|
std::os::unix::fs::symlink(&input, &alias).unwrap();
|
|
assert_two_valid_pdfs(&dir.check(&[&input, &alias]));
|
|
|
|
let file_alias = dir.0.join("page.pdf");
|
|
std::os::unix::fs::symlink(input.join("a.pdf"), &file_alias).unwrap();
|
|
let results = dir.check(&[&file_alias]);
|
|
assert_eq!(results.len(), 1);
|
|
assert_eq!(results[0]["status"], "ok");
|
|
assert_eq!(results[0]["file"], file_alias.to_string_lossy().as_ref());
|
|
}
|