Files
photocraft/crates/engine/tests/explicit_layer_target.rs
Valentin KleinandClaude Opus 5.5 706638b284 Tests: bad-layer-id checks use an id that can't exist (#3343) (#3351)
* Tests: bad-layer-id checks use an id that can't exist (#3343)

Layer ids come from one process-wide counter (shared with document and
smart-object ids, and bumped by ensure_ids_above on file loads). A full
`cargo test` run passes ids like 9999, so a layer a test just created can
get the "bad" id, the command succeeds and the assertion fails at random
(seen on CI in type_extra_cmds::tests::vertical_orientation_renders_columns_inside_the_canvas).

Use u64::MAX, which the counter never reaches, for every "no such layer"
sentinel in the engine and ui-egui tests. Test-only change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Tests: use u64::MAX for bad-layer-id sentinels added on main since (#3343)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 01:59:43 -04:00

75 lines
3.6 KiB
Rust

//! A command that documents `"layer":id` checks its precondition on that layer, not on whichever
//! layer happens to be active (#466 for smart filters, generalised): a script, action or agent can
//! Replace Contents or Rasterize a smart object while another layer is selected.
use photocraft_doc::{LayerContent, LayerId};
use photocraft_engine::{EngineError, Session};
use photocraft_geom::Rect;
use serde_json::json;
/// A smart object (blue) under a plain pixel layer, which is active.
fn session() -> (Session, u64, u64) {
let mut s = Session::new();
s.execute("file.new", json!({"width": 8, "height": 6})).unwrap();
s.execute("layer.new.layer", json!({})).unwrap();
s.edit("paint", |doc, active| {
doc.layer_mut(active.unwrap()).unwrap().surface_mut().unwrap().fill_rect(Rect::new(0, 0, 8, 6), &[0.0, 0.0, 1.0, 1.0]);
Ok(())
})
.unwrap();
let so = s.execute("layer.smartObjects.convertToSmartObject", json!({})).unwrap()["layer"].as_u64().unwrap();
let pixels = s.execute("layer.new.layer", json!({})).unwrap()["layer"].as_u64().unwrap();
(s, so, pixels)
}
fn active(s: &Session) -> u64 {
s.active().unwrap().active_layer.unwrap().0
}
fn kind(s: &Session, id: u64) -> &'static str {
s.active().unwrap().doc.layer(LayerId(id)).unwrap().content.kind_name()
}
#[test]
fn smart_object_commands_act_on_a_named_layer_while_another_is_active() {
let (mut s, so, pixels) = session();
assert_eq!(active(&s), pixels);
// Without "layer" the active pixel layer decides, as the menus show.
assert!(!s.is_enabled("layer.smartObjects.replaceContents"));
// Replace Contents on the named smart object.
let dir = std::env::temp_dir().join(format!("pcraft-explicit-layer-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let png = dir.join("red.png");
let red = photocraft_doc::Document::with_background(
"r",
photocraft_geom::Size::new(8, 6),
photocraft_color::ColorMode::Rgb,
photocraft_color::SampleType::U8,
photocraft_color::Color::rgba(1.0, 0.0, 0.0, 1.0),
);
std::fs::write(&png, photocraft_io::export(&red, "png", &Default::default()).unwrap().bytes).unwrap();
s.execute("layer.smartObjects.replaceContents", json!({"layer": so, "path": png.to_str().unwrap()})).unwrap();
std::fs::remove_dir_all(&dir).ok();
let LayerContent::Smart(sm) = &s.active().unwrap().doc.layer(LayerId(so)).unwrap().content else { panic!("smart object") };
assert_eq!(sm.cache.as_ref().unwrap().rgba(2, 2), [1.0, 0.0, 0.0, 1.0], "the named smart object was replaced");
assert_eq!(active(&s), pixels, "the active layer stays");
// Rasterize it, still without selecting it.
s.execute("layer.rasterize.smartObject", json!({"layer": so})).unwrap();
assert_eq!(kind(&s, so), "Pixel");
assert_eq!(active(&s), pixels);
}
#[test]
fn a_named_layer_of_the_wrong_kind_or_that_does_not_exist_is_refused() {
let (mut s, _so, pixels) = session();
let before = s.active().unwrap().doc.clone();
// The named layer is checked: a pixel layer is not a smart object.
let r = s.execute("layer.smartObjects.rasterize", json!({"layer": pixels}));
assert!(matches!(r, Err(EngineError::Disabled(..))), "{r:?}");
// A layer that doesn't exist falls back to the active layer's check, and still fails.
assert!(s.execute("layer.smartObjects.rasterize", json!({"layer": u64::MAX})).is_err());
assert!(s.execute("layer.smartObjects.rasterize", json!({"layer": "nope"})).is_err());
assert_eq!(s.active().unwrap().doc, before);
assert_eq!(active(&s), pixels);
}