Files
pdfcraft/crates/ui-egui/tests/it/camera.rs
T
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

217 lines
8.7 KiB
Rust
Raw 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.
//! Scan & OCR ▸ Enhance ▸ Camera image in the real shell (egui_kittest): drag a corner, then
//! Enhance page; Cancel and Esc leave the page as it was.
use egui_kittest::Harness;
use egui_kittest::kittest::Queryable;
use pdfcraft_ui_egui::{PdfCraftApp, QuickTool};
const FIXTURE: &[u8] = b"%PDF-1.7
1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj
2 0 obj << /Type /Pages /Kids [3 0 R 4 0 R] /Count 2 /MediaBox [0 0 300 400] >> endobj
3 0 obj << /Type /Page /Parent 2 0 R >> endobj
4 0 obj << /Type /Page /Parent 2 0 R >> endobj
trailer << /Root 1 0 R >>
%%EOF";
fn harness() -> Harness<'static, PdfCraftApp> {
let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| {
let mut app = PdfCraftApp::new();
app.set_option("language", "en").unwrap();
app.open_bytes("doc.pdf", None, FIXTURE.to_vec()).unwrap();
app.set_option("left", "closed").unwrap();
app.set_option("zoom", "100").unwrap();
app
});
h.run_steps(6);
h
}
fn size(h: &Harness<'static, PdfCraftApp>, page: usize) -> (f32, f32) {
let s = h.state();
let p = &s.session.get(s.views[0].id).unwrap().info.pages[page];
(p.width, p.height)
}
fn undo_label(h: &Harness<'static, PdfCraftApp>) -> Option<String> {
let s = h.state();
s.session.get(s.views[0].id).unwrap().can_undo().map(str::to_owned)
}
#[test]
fn dragging_a_corner_then_enhance_page_straightens_the_page() {
let mut h = harness();
assert!(h.state_mut().execute("ocr.enhance_camera"));
assert_eq!(h.state().quick_tool, QuickTool::EnhanceCamera);
h.run_steps(3);
let quad = h.state().views[0].camera.expect("the corners start on the current page");
assert_eq!(quad.page, 0);
assert_eq!(quad.corners, [[15.0, 20.0], [285.0, 20.0], [285.0, 380.0], [15.0, 380.0]], "inset by 5%");
// Drag the top-left corner to (60, 80) on the page.
let r = h.state().views[0].page_screen_rect(0).unwrap();
let (a, b) = (r.min + r.size() * 0.05, r.min + r.size() * 0.2);
h.hover_at(a);
h.run_steps(1);
h.drag_at(a);
h.run_steps(1);
for k in 1..=4 {
h.hover_at(a + (b - a) * (k as f32 / 4.0));
h.run_steps(1);
}
h.drop_at(b);
h.run_steps(2);
let c = h.state().views[0].camera.unwrap().corners[0];
assert!((c[0] - 60.0).abs() < 1.0 && (c[1] - 80.0).abs() < 1.0, "{c:?}");
assert_eq!(undo_label(&h), None, "dragging changes nothing yet");
h.get_by_label("Enhance page").click();
h.run_steps(4);
// The longer of each pair of opposite sides: 270 × 360 pt.
let (w, hh) = size(&h, 0);
assert!((w - 270.0).abs() < 2.0 && (hh - 360.0).abs() < 2.0, "{w}×{hh}");
assert_eq!(size(&h, 1), (300.0, 400.0), "only the page with the corners changes");
assert_eq!(undo_label(&h).as_deref(), Some("Enhance camera image"));
// The tool stays open for the enhanced page: turn it, then Done.
assert_eq!(h.state().quick_tool, QuickTool::EnhanceCamera);
assert!(h.state().views[0].camera.is_none());
assert_eq!(h.state().views[0].camera_done.map(|d| d.page), Some(0));
h.get_by_label("Rotate clockwise").click();
h.run_steps(4);
let (w, hh) = size(&h, 0);
assert!((w - 360.0).abs() < 2.0 && (hh - 270.0).abs() < 2.0, "{w}×{hh}");
assert_eq!(undo_label(&h).as_deref(), Some("Rotate page"));
h.get_by_label("Done").click();
h.run_steps(3);
assert_eq!(h.state().quick_tool, QuickTool::Select);
assert_eq!(h.state().views[0].camera_done, None);
}
#[test]
fn the_clean_up_level_enhances_again_from_the_photo_and_keeps_the_turn() {
let mut h = harness();
assert!(h.state_mut().execute("ocr.enhance_camera"));
h.run_steps(3);
h.get_by_label("Enhance page").click();
h.run_steps(4);
let done = h.state().views[0].camera_done.unwrap();
assert_eq!((done.look, done.level, done.steps), (pdfcraft_engine::camera_clean::Look::Colour, 50, 1), "enhanced in colour at the default level");
h.get_by_label("Rotate clockwise").click();
h.run_steps(4);
let turned = size(&h, 0);
assert!(turned.0 > turned.1, "{turned:?}");
// Let go of the slider at 100.
let view = &mut h.state_mut().views[0];
if let Some(d) = view.camera_done.as_mut() {
d.level = 100;
}
view.pending_action = Some(pdfcraft_ui_egui::canvas::ViewAction::CameraLevel);
h.run_steps(4);
let done = h.state().views[0].camera_done.unwrap();
assert_eq!((done.applied.1, done.steps, done.turns), (100, 2, 90));
assert_eq!(size(&h, 0), turned, "still turned");
// The history holds one enhancement and one turn, not a pile of them.
let s = h.state_mut();
let id = s.views[0].id;
assert_eq!(s.session.undo(id).unwrap().as_str(), "Rotate page");
assert_eq!(s.session.undo(id).unwrap().as_str(), "Enhance camera image");
assert_eq!(s.session.get(id).unwrap().can_undo(), None);
let p = &s.session.get(id).unwrap().info.pages[0];
assert_eq!((p.width, p.height), (300.0, 400.0), "back to the photo");
}
#[test]
fn choosing_a_look_enhances_again() {
use pdfcraft_engine::camera_clean::Look;
let mut h = harness();
assert!(h.state_mut().execute("ocr.enhance_camera"));
h.run_steps(3);
h.get_by_label("Enhance page").click();
h.run_steps(4);
for (label, look) in [("Black and white", Look::BlackWhite), ("Greyscale", Look::Greyscale), ("Original", Look::Original)] {
h.get_by_label(label).click();
h.run_steps(4);
let done = h.state().views[0].camera_done.unwrap();
assert_eq!((done.applied.0, done.steps), (look, 1), "{label}");
let s = h.state();
assert_eq!(s.session.get(s.views[0].id).unwrap().can_undo(), Some("Enhance camera image"), "{label}");
}
}
#[test]
fn after_enhance_page_crop_and_organize_take_over() {
for (button, then) in [("Crop pages", QuickTool::Crop), ("Organize pages", QuickTool::Select)] {
let mut h = harness();
assert!(h.state_mut().execute("ocr.enhance_camera"));
h.run_steps(3);
h.get_by_label("Enhance page").click();
h.run_steps(4);
assert_eq!(h.state().views[0].camera_done.map(|d| d.page), Some(0));
h.get_by_label(button).click();
h.run_steps(3);
assert_eq!(h.state().quick_tool, then, "{button}");
assert_eq!(h.state().views[0].camera_done, None, "{button}");
assert_eq!(h.state().views[0].organize, button == "Organize pages");
}
}
#[test]
fn cancel_and_escape_leave_the_page_alone() {
let mut h = harness();
assert!(h.state_mut().execute("ocr.enhance_camera"));
h.run_steps(3);
h.get_by_label("Cancel").click();
h.run_steps(3);
assert_eq!(h.state().quick_tool, QuickTool::Select);
assert!(h.state().views[0].camera.is_none());
assert!(h.state_mut().execute("ocr.enhance_camera"));
h.run_steps(3);
assert!(h.state().views[0].camera.is_some());
h.key_press(egui::Key::Escape);
h.run_steps(3);
assert_eq!(h.state().quick_tool, QuickTool::Select);
assert!(h.state().views[0].camera.is_none());
assert_eq!(size(&h, 0), (300.0, 400.0));
assert_eq!(undo_label(&h), None);
}
/// A turn made outside the tool after the enhancement breaks its run in the history: changing the
/// level then leaves the page and the history as they are instead of enhancing the straightened
/// page a second time.
#[test]
fn the_level_leaves_the_page_alone_when_another_edit_came_in_between() {
let mut h = harness();
assert!(h.state_mut().execute("ocr.enhance_camera"));
h.run_steps(3);
h.get_by_label("Enhance page").click();
h.run_steps(4);
h.get_by_label("Rotate clockwise").click();
h.run_steps(4);
// Another "Rotate page" from outside the tool, with the same history label.
{
let s = h.state_mut();
let id = s.views[0].id;
s.session.apply(id, pdfcraft_engine::Edit::RotatePages { pages: vec![0], degrees: 90 }).unwrap();
}
h.run_steps(2);
let before = size(&h, 0);
let applied = h.state().views[0].camera_done.unwrap().applied;
let view = &mut h.state_mut().views[0];
if let Some(d) = view.camera_done.as_mut() {
d.level = 100;
}
view.pending_action = Some(pdfcraft_ui_egui::canvas::ViewAction::CameraLevel);
h.run_steps(4);
assert_eq!(size(&h, 0), before, "the page is not warped again");
let done = h.state().views[0].camera_done.unwrap();
assert_eq!((done.level, done.applied), (applied.1, applied), "the slider goes back");
let s = h.state_mut();
let id = s.views[0].id;
assert_eq!(s.session.undo(id).unwrap().as_str(), "Rotate page");
assert_eq!(s.session.undo(id).unwrap().as_str(), "Rotate page");
assert_eq!(s.session.undo(id).unwrap().as_str(), "Enhance camera image");
assert_eq!(s.session.get(id).unwrap().can_undo(), None);
}