Session::execute catches a command's panic and returns an ordinary Err (the
last-resort guard in jobs.rs), so the unwind never reached panic_hunt's own
catch_unwind and a panicking command counted as a pass.
The harness now installs a panic hook that counts every panic in the process,
including on rayon workers, and a run fails if the count moved. Runs are
serialised so a panic is charged to the run that caused it. No production
code changes.
Test: contained_panics_count_and_graceful_errors_do_not. A contained panic
fails the run and a graceful Err passes; the first assertion fails without
the hook. The full --ignored fuzz stays green, so no command panics today.
Co-authored-by: prekabreki <prekabreki@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* panic_hunt: don't fuzz the print commands into a real printer
file.print and file.printOneCopy spool to CUPS, so every run of the adversarial-params test
printed a blank page on the default printer. The hunt now skips those two ids (their params
are validated like everyone else's; spooling is not what the test is for).
* Guard the print spooler itself and the CI environment
- print_cmds: disable_printing() (a process-wide flag the panic hunt sets) and the
PHOTOCRAFT_NO_PRINT environment variable make spool_pdf refuse before running lp; dryRun
still renders and reports the command. Test.
- CI: PHOTOCRAFT_NO_PRINT=1 and LPDEST=photocraft-no-such-printer, so a regression fails
loudly instead of printing. Documented in docs/development.md › Environment variables.
- photocraft-winprint: an isolated unsafe crate (like photocraft-tablet)
for Windows printing: printer list, the driver's Print Settings dialog,
page geometry, and GDI printing with ICM off.
- file.print prints on Windows (explicit "send": true only); new
file.print.printers and file.print.page commands; printerSettings param.
- Print dialog on Windows: Printer menu, Print Settings…, paper from the
driver.
- panic_hunt fuzzes print commands as dry runs.
Closes#2697.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first type.create starts the shared text engine, which scans every
system font once (3.5-5 s under load). panic_hunt billed that to
type.create against its 4 s deadline and failed intermittently. A real
hang reproduces, one-time setup does not: a timeout now counts only if a
rerun on a fresh session times out too.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
CI's `cargo fmt --all -- --check` has failed on every run because the
repo had no rustfmt config and was never formatted. Use the same settings
as EffectCraft/FilmCraft (max_width 160, use_small_heuristics Max), which
also keeps the churn small. Formatting only; no code changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
warp_gray cleared the old + warped region as one dense buffer spanning their
union, so moving a selection/mask/channel far away (e.g. dx=1e5) allocated a
~1e5x1e5 buffer and hung the app. Write the two regions separately instead —
Surface is tile-sparse, so a distant region costs only its own tiles. Found by
the panic_hunt fuzz (now also catches hangs via a per-command thread timeout).
Regression test covers a far transform.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rule 9 (no panic on input). Fixes found via fuzzing:
- edit.paste: a short/non-array "center" (e.g. [5], []) indexed a[1] and
panicked; now requires len>=2, else falls back to the canvas centre.
- edit.transform.again: a quad row with <2 columns indexed [1] and panicked;
now rejected with the other shape checks.
Tests: paste_with_malformed_center_does_not_panic (fast regression) plus
tests/panic_hunt.rs, an opt-in (#[ignore], run --ignored) fuzz that dispatches
every command with adversarial params and asserts none panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>