mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-14 19:26:56 -04:00
Shipped code no longer unwraps, expects or panics: every one of the 124 unwrap()/expect()/panic!/unreachable! sites outside tests now handles the failure (Result/Option, ?, let-else, defaults) and reports errors to the user. - Lints: workspace clippy denies unwrap_used, expect_used, panic, todo, unimplemented and unreachable; clippy.toml allows them in tests, and integration tests, examples and testkit opt out explicitly. - Safety net (vectorcraft_engine::guard): a panic in a top-level command rolls the document back and returns EngineError::Internal; a panicking tool is reset; the MCP server answers with an internal error and keeps serving; the UI frame and each control request are guarded. - APIs that hid failures now return them: Rendered::to_png/to_jpeg/to_webp return Result (JPEG/WebP errors used to write empty files), FontDb::face returns Option, Region::top returns Option. - Fuzzing: engine/tests/import_fuzz.rs feeds garbage, hostile and mutated SVG and PDF through import → render → export (30k cases locally, no panics). - Docs: AGENTS.md/CLAUDE.md rule, docs/development.md "Robustness" section, README pointer and a PR template checklist. Co-Authored-By: Claude Opus 5.5 <[email protected]>
60 lines
2.4 KiB
Rust
60 lines
2.4 KiB
Rust
//! Minimal reproductions of engine bugs found by the property tests. Each is `#[ignore]`d with a
|
||
//! `BUG:` reason until fixed; remove the attribute when the fix lands.
|
||
// Integration tests: unwrapping and panicking on failure is fine here, unlike in shipped code (AGENTS.md › Robustness).
|
||
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
|
||
|
||
use serde_json::json;
|
||
use vectorcraft_engine::Session;
|
||
use vectorcraft_testkit::fixtures::{exec, rect, select, session};
|
||
use vectorcraft_testkit::invariants::{check_native_roundtrip_exact, check_session, doc_json, first_diff};
|
||
|
||
#[test]
|
||
fn bug_group_inside_compound() {
|
||
let mut s = session();
|
||
let a = rect(&mut s, 0.0, 0.0, 10.0, 10.0);
|
||
let b = rect(&mut s, 20.0, 0.0, 10.0, 10.0);
|
||
select(&mut s, &[a, b]);
|
||
exec(&mut s, "object.compoundPath.make", json!({}));
|
||
select(&mut s, &[a]);
|
||
let _ = s.execute("object.group", &json!({}));
|
||
check_session(&s).unwrap();
|
||
}
|
||
|
||
#[test]
|
||
fn bug_nested_commands_double_journal() {
|
||
let mut s = session();
|
||
rect(&mut s, 0.0, 0.0, 10.0, 10.0);
|
||
exec(&mut s, "transparency.set", json!({"opacity": 0}));
|
||
exec(&mut s, "edit.undo", json!({}));
|
||
let ids: Vec<&str> = s.journal.iter().map(|(id, _)| id.as_str()).collect();
|
||
assert_eq!(ids, ["file.new", "shape.rectangle", "transparency.set", "edit.undo"], "journal: {ids:?}");
|
||
let mut r = Session::new();
|
||
for (id, p) in &s.journal.clone() {
|
||
let _ = r.execute(id, p);
|
||
}
|
||
let (want, got) = (doc_json(&s.doc().unwrap().doc), doc_json(&r.doc().unwrap().doc));
|
||
assert_eq!(want, got, "{}", first_diff(&want, &got, "$"));
|
||
}
|
||
|
||
#[test]
|
||
fn bug_native_roundtrip_not_bit_exact() {
|
||
let mut s = session();
|
||
exec(&mut s, "shape.ellipse", json!({"x": 100.1, "y": 60.3, "width": 120.7, "height": 120.9}));
|
||
exec(&mut s, "object.rotate", json!({"angle": 33.3}));
|
||
check_native_roundtrip_exact(&s.doc().unwrap().doc).unwrap();
|
||
}
|
||
|
||
#[test]
|
||
fn bug_bring_to_front_across_layers_underflows() {
|
||
let mut s = session();
|
||
let a = rect(&mut s, 0.0, 0.0, 10.0, 10.0);
|
||
exec(&mut s, "layer.new", json!({}));
|
||
let b = rect(&mut s, 20.0, 0.0, 10.0, 10.0);
|
||
for cmd in ["object.arrange.bringToFront", "object.arrange.bringForward"] {
|
||
select(&mut s, &[a, b]);
|
||
let r = vectorcraft_testkit::catch_quiet(|| s.execute(cmd, &json!({})));
|
||
assert!(r.is_ok(), "{cmd} panicked: {:?}", r.err());
|
||
check_session(&s).unwrap();
|
||
}
|
||
}
|