Merge upstream main 924c4da4a into next

This commit is contained in:
aiongg
2026-10-17 01:59:44 +00:00
3 changed files with 75 additions and 2 deletions
+11 -1
View File
@@ -11,7 +11,17 @@ fn result(auto: &mut Automation, args: Value) -> Value {
// Fresh fixtures include the current timestamp; retain their bytes for output comparisons.
fn pdf() -> Vec<u8> {
Session::new().create_from_text("Original", "Original text").unwrap().as_ref().clone()
// Reuse one synthetic document so byte comparisons cannot straddle the
// writer's modification-time clock. Each caller gets an independent buffer.
static PDF: std::sync::OnceLock<Vec<u8>> = std::sync::OnceLock::new();
PDF.get_or_init(|| Session::new().create_from_text("Original", "Original text").unwrap().as_ref().clone()).clone()
}
#[test]
fn pdf_fixture_remains_identical_across_metadata_clock_tick() {
let before = pdf();
std::thread::sleep(std::time::Duration::from_millis(1100));
assert_eq!(before, pdf());
}
#[test]
+2 -1
View File
@@ -506,7 +506,8 @@ mod cups_options {
let Some(record) = std::env::var_os(RECORD) else { return };
let args: Vec<String> = std::env::args().skip_while(|a| a != "--").skip(1).collect();
std::fs::write(std::path::Path::new(&record).join("args"), args.join("\n")).unwrap();
println!("{}", std::env::var(ANSWER).unwrap());
// Serial libtest prints its test label without a newline before this helper's stdout.
println!("\n{}", std::env::var(ANSWER).unwrap());
}
fn command(record: &std::path::Path, answer: &str) -> std::process::Command {
+62
View File
@@ -608,3 +608,65 @@ fn tool_picker_keeps_picked_recipient_when_other_open_is_ready() {
}
}
}
#[test]
#[allow(clippy::expect_used, clippy::unwrap_used)]
fn inline_tool_open_keeps_its_command_for_pdf_conversion_and_password_retry() {
for (name, bytes, encrypted) in
[("plain.pdf", fixture(1), false), ("converted.txt", b"Text to export".to_vec(), false), ("locked.pdf", encrypted_tool_fixture(), true)]
{
let path = std::env::temp_dir().join(format!("pdfcraft-inline-tool-{}-{name}", std::process::id()));
std::fs::write(&path, bytes).expect("write selected file");
let mut app = PdfCraftApp::new();
app.set_option("language", "en").expect("language preference");
assert!(app.ctx.is_none(), "this exercises the no-context inline path");
app.open_path_then(&path.to_string_lossy(), Some("export.text"));
if encrypted {
assert!(app.dialog.is_none());
assert_eq!(app.password_prompt.as_ref().expect("password prompt").then, Some("export.text"));
app.submit_password(Some("wrong".into()));
assert!(app.password_prompt.as_ref().expect("wrong-password prompt").error.is_some());
assert!(app.dialog.is_none());
app.submit_password(Some("tool-password".into()));
}
assert_eq!(app.views.len(), 1);
assert_eq!(app.active, Some(0));
assert!(matches!(app.dialog, Some(crate::Dialog::Export(_))), "the inline {name} retains its export command");
let _ = std::fs::remove_file(path);
}
}
#[test]
#[allow(clippy::expect_used, clippy::panic, clippy::unwrap_used)]
fn startup_files_wait_for_absolute_link_consent_and_preparation() {
let (mut h, _) = harness();
let source = h.state().views[0].id;
let path = std::env::temp_dir().join(format!("pdfcraft-open-consented-startup-{}.pdf", std::process::id()));
std::fs::write(&path, fixture(2)).expect("write linked PDF");
h.state_mut().open_remote_link(source, path.to_string_lossy().into_owned(), pdfcraft_render::RemoteDestination::Default);
h.state().startup_inbox.lock().expect("startup queue").push(("startup.pdf".into(), fixture(1)));
h.step();
assert!(h.state().pending_remote_link.is_some());
assert_eq!(h.state().startup_inbox.lock().expect("startup queue").len(), 1);
assert_eq!(h.state().views.len(), 1, "startup cannot replace the consent interaction");
let (gate, started, release, finished) = open_worker::prepare_gate();
h.state_mut().open_prepare_gate = Some(gate);
h.state_mut().resolve_pending_remote_link(true);
started.recv_timeout(Duration::from_secs(5)).expect("worker reached prepare barrier");
h.step();
let queued = h.state().startup_inbox.lock().expect("startup queue").len();
let visible = h.state().views.len();
// Always release before asserting: a failure must not strand the worker at the barrier.
release.send(()).expect("release prepare barrier");
finished.recv_timeout(Duration::from_secs(5)).expect("worker finished preparation");
assert_eq!(queued, 1, "startup bytes wait while the approved linked PDF prepares");
assert_eq!(visible, 1);
h.step();
let app = h.state();
assert_eq!(app.views.len(), 3);
assert_eq!(app.session.get(app.views[1].id).expect("linked document").path.as_deref(), path.to_str());
assert_eq!(app.session.get(app.views[2].id).expect("startup document").name, "startup.pdf");
assert_eq!(app.active, Some(1), "the user's approved navigation keeps focus");
let _ = std::fs::remove_file(path);
}