From 6fb5d0202ca784d9a7cd8accd2a797adf4d0d9c7 Mon Sep 17 00:00:00 2001 From: skylar taylor-barrick Date: Thu, 22 Oct 2026 14:32:32 -0600 Subject: [PATCH] #246: test that start-up and the panic hook never use eprintln eprintln! panics when standard error is a broken pipe, and a panic inside the panic hook aborts the process. The test scans the product code of the desktop main.rs (everything before the test module) and fails on any eprintln! outside a comment. With the old eprintln! calls restored in the panic hook and the log-file warning, it fails listing main.rs:188 and main.rs:227. --- apps/pdfcraft/src/main.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/apps/pdfcraft/src/main.rs b/apps/pdfcraft/src/main.rs index 1a36d64bf..420683942 100644 --- a/apps/pdfcraft/src/main.rs +++ b/apps/pdfcraft/src/main.rs @@ -1414,4 +1414,26 @@ mod tests { let class = entry.lines().find_map(|line| line.strip_prefix("StartupWMClass=")); assert_eq!(class, Some(super::APP_ID)); } + + // Regression test for #246: `eprintln!` panics when standard error is a broken pipe (a GUI + // launch on Windows), and a panic inside the panic hook aborts the process. The start-up + // path and the hook must write to standard error without it. + #[test] + fn start_up_and_the_panic_hook_never_use_eprintln() { + let source = include_str!("main.rs"); + // Everything before the top-level test module is product code. + let product = source.split("\n#[cfg(test)]\nmod tests {").next().unwrap_or(source); + assert!(product.len() < source.len(), "found the end of the product code"); + let offenders: Vec<_> = product + .lines() + .enumerate() + .filter(|(_, line)| !line.trim_start().starts_with("//") && line.contains("eprintln!")) + .map(|(n, line)| format!("main.rs:{}: {}", n + 1, line.trim())) + .collect(); + assert!( + offenders.is_empty(), + "`eprintln!` panics on a broken standard error pipe; use `log::warn!` or `write_fmt` on `stderr()` and ignore the error:\n{}", + offenders.join("\n") + ); + } }