Files
aiongg 9526363152 Build each crate's integration tests as one binary (#4087)
* Build each crate's integration tests as one binary

Every tests/*.rs file is its own executable that links the whole crate
graph. Move each crate's test files into tests/it/ as modules of one
tests/it/main.rs, for apps/pdfcraft, apps/pdfcraft-cli, automation, cos,
crypt, engine, render, sign and ui-egui. ui-egui's ui_group binary and
the support modules move into tests/it/ too. Crates with a single test
file keep it. The test executables go from 150 to 52, and every crate
runs the same number of tests as before.

ui-egui perf keeps its own binary (own process): its frame-time budgets
would charge each measurement for the other tests running beside it.

What the move needs:
- include_str!/include_bytes! and #[path] paths gain one "../".
- ui-egui: one GPU lock in tests/it/main.rs replaces the five per-file
  ones (content, document_links, editing, fill_sign, view), so one
  process creates one wgpu device at a time. The drop-line test in ui.rs
  and the screenshot blocks behind PDFCRAFT_*_SHOT(S) and
  CRAFT_UI_DOCKING_DIR, which render without holding it, take it too.
- pdfcraft winit_wayland_scroll: the vendored winit source it compiles
  names crate::event, so that shim lives in main.rs.
- parity evidence names the new paths.

* Name the one-binary test paths in docs and comments

- READMEs, docs, ATTRIBUTION and source comments name tests/it/<file>.rs.
- The oracle README's example filters with --test it encryption::qpdf_agrees.
- pdfcraft's GPU lock comment points at the UI tests' shared lock in
  crates/ui-egui/tests/it/main.rs.
2026-10-22 11:28:58 -04:00

115 lines
5.0 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! `render --out` writes the file format its name says (#248): a `.png` is a PNG, not a netpbm
//! PAM stream with a `.png` extension.
use std::path::PathBuf;
use std::process::{Command, Output};
const BIN: &str = env!("CARGO_BIN_EXE_pdfcraft-cli");
fn tmp(test: &str, name: &str) -> PathBuf {
let d = std::env::temp_dir().join(format!("pdfcraft-cli-render-{test}-{}", std::process::id()));
std::fs::create_dir_all(&d).unwrap();
d.join(name)
}
/// A one-page PDF with some text, as `--out` would be given it.
fn fixture(test: &str) -> PathBuf {
let pdf = tmp(test, "in.pdf");
let bytes = pdfcraft_engine::Session::new().create_from_text("Render test", "Hello from PdfCraft").unwrap();
std::fs::write(&pdf, bytes.as_slice()).unwrap();
pdf
}
fn render(pdf: &std::path::Path, out: &std::path::Path) -> Output {
Command::new(BIN).args(["render", pdf.to_str().unwrap(), "--page", "1", "--dpi", "36", "--out", out.to_str().unwrap()]).output().unwrap()
}
fn be32(b: &[u8]) -> u32 {
u32::from_be_bytes([b[0], b[1], b[2], b[3]])
}
#[test]
fn a_png_name_gets_a_png() {
let pdf = fixture("png");
let out = tmp("png", "p1.png");
let r = render(&pdf, &out);
assert!(r.status.success(), "{}", String::from_utf8_lossy(&r.stderr));
let bytes = std::fs::read(&out).unwrap();
assert_eq!(&bytes[..8], b"\x89PNG\r\n\x1a\n", "not a PNG: {:?}", &bytes[..bytes.len().min(16)]);
// IHDR: the page is 612×792 pt, rendered at 36 dpi
assert_eq!(&bytes[12..16], b"IHDR");
assert_eq!((be32(&bytes[16..20]), be32(&bytes[20..24])), (306, 396));
// the reported size matches the file
let stderr = String::from_utf8_lossy(&r.stderr);
assert!(stderr.contains("306×396"), "{stderr}");
}
#[test]
fn jpeg_and_tiff_names_get_those_formats() {
let pdf = fixture("jpg");
let jpg = tmp("jpg", "p1.JPG");
assert!(render(&pdf, &jpg).status.success());
assert_eq!(&std::fs::read(&jpg).unwrap()[..2], b"\xff\xd8");
let tif = tmp("jpg", "p1.tif");
assert!(render(&pdf, &tif).status.success());
let bytes = std::fs::read(&tif).unwrap();
assert!(bytes.starts_with(b"II*\0") || bytes.starts_with(b"MM\0*"), "not a TIFF: {:?}", &bytes[..4]);
}
#[test]
fn the_file_records_the_dpi_it_was_rendered_at() {
// The engine's tests check each format; this checks that `--dpi` reaches the file.
let pdf = fixture("dpi");
let jpg = tmp("dpi", "p1.jpg");
assert!(render(&pdf, &jpg).status.success());
let bytes = std::fs::read(&jpg).unwrap();
assert_eq!(&bytes[6..11], b"JFIF\0");
assert_eq!(bytes[13..18], [1, 0, 36, 0, 36], "JFIF density in dots per inch");
}
#[test]
fn a_pam_name_still_gets_the_netpbm_stream() {
let pdf = fixture("pam");
let out = tmp("pam", "p1.pam");
assert!(render(&pdf, &out).status.success());
let bytes = std::fs::read(&out).unwrap();
let header = b"P7\nWIDTH 306\nHEIGHT 396\nDEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n";
assert!(bytes.starts_with(header), "{:?}", String::from_utf8_lossy(&bytes[..bytes.len().min(80)]));
assert_eq!(bytes.len(), header.len() + 306 * 396 * 4);
}
#[test]
fn an_unknown_extension_fails_and_writes_nothing() {
let pdf = fixture("bmp");
let out = tmp("bmp", "p1.bmp");
let r = render(&pdf, &out);
assert!(!r.status.success());
let stderr = String::from_utf8_lossy(&r.stderr);
assert!(stderr.contains(".png, .jpg, .tif or .pam"), "{stderr}");
assert!(!out.exists());
}
/// `--page` and `--dpi` are checked before the file is opened. A page that is not a positive
/// whole number names the same expectation `text` names; a dpi that parses as an `f32` but
/// cannot be rendered (`0`, a negative value, `NaN`) names the option and the value, instead
/// of reaching the renderer as a scale it refuses with its own internal message.
#[test]
fn a_page_or_dpi_the_renderer_cannot_use_is_refused_before_reading_the_file() {
for (option, value, diagnostic) in [
("--page", "abc", "bad --page: expected a positive page number"),
("--page", "0", "bad --page: expected a positive page number"),
("--dpi", "abc", "bad --dpi: expected a positive number of dots per inch, got \"abc\""),
("--dpi", "0", "bad --dpi: expected a positive number of dots per inch, got \"0\""),
("--dpi", "-3", "bad --dpi: expected a positive number of dots per inch, got \"-3\""),
("--dpi", "NaN", "bad --dpi: expected a positive number of dots per inch, got \"NaN\""),
("--dpi", "inf", "bad --dpi: expected a positive number of dots per inch, got \"inf\""),
] {
let out = tmp("refuse", "out.png");
// `missing.pdf` is never read: the option's own message is the error.
let r = Command::new(BIN).args(["render", "missing.pdf", "--out", out.to_str().unwrap(), option, value]).output().unwrap();
assert_eq!(r.status.code(), Some(1), "{option} {value}");
assert_eq!(r.stderr, format!("pdfcraft-cli: {diagnostic}\n").as_bytes(), "{option} {value}");
assert!(!out.exists(), "{option} {value}: no output may be written");
}
}