mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-09 11:53:49 +00:00
fix(xtask): render-oracle writes page pixels into a private temporary folder (#5988)
This commit is contained in:
+51
-3
@@ -323,6 +323,35 @@ pub fn text_oracle(args: &[String]) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A new folder in the temporary directory that only this user can open (0700 on Unix), removed
|
||||
/// when dropped, on success and on error alike.
|
||||
struct PrivateDir(PathBuf);
|
||||
|
||||
impl PrivateDir {
|
||||
fn new(prefix: &str) -> anyhow::Result<Self> {
|
||||
let nanos = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).map_or(0, |d| d.subsec_nanos());
|
||||
for attempt in 0..100u32 {
|
||||
let path = std::env::temp_dir().join(format!("{prefix}-{}-{nanos}-{attempt}", std::process::id()));
|
||||
let mut builder = std::fs::DirBuilder::new();
|
||||
#[cfg(unix)]
|
||||
std::os::unix::fs::DirBuilderExt::mode(&mut builder, 0o700);
|
||||
// `create`, not `create_all`: a name that is already there is never used.
|
||||
match builder.create(&path) {
|
||||
Ok(()) => return Ok(Self(path)),
|
||||
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => continue,
|
||||
Err(e) => return Err(e).with_context(|| format!("create {}", path.display())),
|
||||
}
|
||||
}
|
||||
bail!("no unused temporary folder name for {prefix}")
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PrivateDir {
|
||||
fn drop(&mut self) {
|
||||
let _ = std::fs::remove_dir_all(&self.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Compare our rendering against Poppler's, page 1 of every PDF in a directory.
|
||||
///
|
||||
/// This is the measured half of the fidelity gap (`docs/gaps.md` gap 12): until it exists, every
|
||||
@@ -368,7 +397,10 @@ pub fn render_oracle(args: &[String]) -> anyhow::Result<()> {
|
||||
.collect();
|
||||
files.sort();
|
||||
|
||||
let out_pam = std::env::temp_dir().join(format!("pdfcraft-render-oracle-{}.pam", std::process::id()));
|
||||
// The page's pixels go into a folder only this user can open, made for this run (never an
|
||||
// existing one, which another user could have put a link in), and removed with it.
|
||||
let private = PrivateDir::new("pdfcraft-render-oracle")?;
|
||||
let out_pam = private.0.join("page.pam");
|
||||
let (mut scores, mut sizes_differed, mut ours_failed, mut theirs_failed) = (Vec::new(), 0usize, 0usize, 0usize);
|
||||
let mut worst: Vec<(f64, String)> = Vec::new();
|
||||
for file in files.iter().take(limit) {
|
||||
@@ -410,7 +442,7 @@ pub fn render_oracle(args: &[String]) -> anyhow::Result<()> {
|
||||
Err(_) => sizes_differed = sizes_differed.saturating_add(1),
|
||||
}
|
||||
}
|
||||
let _ = std::fs::remove_file(&out_pam);
|
||||
drop(private);
|
||||
|
||||
if scores.is_empty() {
|
||||
bail!(
|
||||
@@ -551,7 +583,23 @@ fn f1(a: &BTreeMap<String, usize>, b: &BTreeMap<String, usize>) -> f64 {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{f1, order_score, root, word_sequence, words};
|
||||
use super::{PrivateDir, f1, order_score, root, word_sequence, words};
|
||||
|
||||
/// Each run gets a new folder of its own, private to the user, gone when the run ends.
|
||||
#[test]
|
||||
fn the_oracle_folder_is_new_private_and_removed() {
|
||||
let (a, b) = (PrivateDir::new("pdfcraft-private-test").unwrap(), PrivateDir::new("pdfcraft-private-test").unwrap());
|
||||
assert_ne!(a.0, b.0);
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
assert_eq!(std::fs::metadata(&a.0).unwrap().permissions().mode() & 0o777, 0o700);
|
||||
}
|
||||
let path = a.0.clone();
|
||||
std::fs::write(path.join("page.pam"), b"x").unwrap();
|
||||
drop(a);
|
||||
assert!(!path.exists());
|
||||
}
|
||||
|
||||
/// The two scores answer different questions, and the point of adding the second one is the
|
||||
/// first row: F1 cannot tell a page from the same page read backwards.
|
||||
|
||||
Reference in New Issue
Block a user