Files
pdfcraft/apps/pdfcraft/tests/help.rs
T
Ridwan 857c491360 fix(app): pdfcraft --help prints usage instead of opening the app (#4076) (#4093)
The desktop app's argument loop took `--help` as a view setting named
`help` (consuming the next argument as its value) and `-h` as a file to
open, so both started the window and a scripted call never returned.

`-h` and `--help` now print a short usage, which points scripts to
pdfcraft-cli, and exit 0 before any window or log file is created.
`--version` and `--help` write through a plain `writeln!` instead of
`println!`, which panicked (exit 101) when standard output was full or
closed, e.g. `pdfcraft --version > /dev/full`.

Release builds on Windows are GUI-subsystem programs, so, as with
`--version` already, the text shows when the output is redirected or
piped but not in a plain console window.

Fixes #4076
2026-10-23 10:43:14 -04:00

56 lines
2.4 KiB
Rust

//! `--help`, `-h` and `--version` print and exit without opening a window (#4076), so a script that
//! calls them returns.
use std::process::{Command, Output, Stdio};
use std::time::{Duration, Instant};
const BIN: &str = env!("CARGO_BIN_EXE_pdfcraft");
/// Run the app with `args`, failing if it is still running after 60 s (it opened its window).
fn run(args: &[&str], stdout: Stdio) -> Output {
let mut child = Command::new(BIN).args(args).stdin(Stdio::null()).stdout(stdout).stderr(Stdio::piped()).spawn().unwrap();
let deadline = Instant::now() + Duration::from_secs(60);
while child.try_wait().unwrap().is_none() {
if Instant::now() > deadline {
let _ = child.kill();
panic!("pdfcraft {args:?} did not exit: it started the app instead");
}
std::thread::sleep(Duration::from_millis(50));
}
child.wait_with_output().unwrap()
}
#[test]
fn help_prints_usage_and_exits() {
for flag in ["--help", "-h"] {
let out = run(&[flag], Stdio::piped());
assert!(out.status.success(), "{flag}: {:?} {}", out.status, String::from_utf8_lossy(&out.stderr));
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(stdout.starts_with("Usage: pdfcraft [options] [files…]"), "{flag}: {stdout}");
assert!(stdout.contains("pdfcraft-cli"), "{flag}: points scripts to the CLI: {stdout}");
}
// Wherever it is on the command line, and with files named.
let out = run(&["missing.pdf", "--help"], Stdio::piped());
assert!(out.status.success());
assert!(String::from_utf8_lossy(&out.stdout).starts_with("Usage:"));
}
#[test]
fn version_prints_the_version_and_exits() {
let out = run(&["--version"], Stdio::piped());
assert!(out.status.success(), "{:?} {}", out.status, String::from_utf8_lossy(&out.stderr));
assert!(String::from_utf8_lossy(&out.stdout).starts_with("pdfcraft "), "{}", String::from_utf8_lossy(&out.stdout));
}
/// A standard output that can't be written (here a full device) ends the run normally: `println!`
/// would panic and exit 101.
#[cfg(target_os = "linux")]
#[test]
fn help_and_version_survive_an_unwritable_stdout() {
for flag in ["--help", "--version"] {
let full = std::fs::OpenOptions::new().write(true).open("/dev/full").unwrap();
let out = run(&[flag], Stdio::from(full));
assert!(out.status.success(), "{flag}: {:?} {}", out.status, String::from_utf8_lossy(&out.stderr));
}
}