forked from 1b-agent/opencadstudio-upstream
test(app): add chprop integration tests for handler dispatch and undo
Add a new #[cfg(test)] mod chprop_integration_tests to src/app/properties.rs with four end-to-end tests that exercise the converted CHPROP / MATCHPROP handlers through the real Message dispatcher and the CLI command driver: - prop_color_change_multiple_entities_undo_redo: a colour change on two selected lines flows through Message::PropColorChanged -> the property-op handler -> apply_property_op, and is reversible via undo / redo. Seeds the panel's source_handles (selected_handles() is empty in this test setup) so the converted non-empty branch runs. - prop_change_on_locked_layer_is_ignored: when a selected entity's layer is locked, property_target_handles filters it out and the handler's empty-handles branch leaves the entity itself untouched. Asserts the entity-level invariant only; the empty branch updates the document header (CECOLOR) and sets dirty as a separate, pre-existing behaviour. - ribbon_lineweight_updates_after_change: the Home-ribbon Lineweight chip is updated by the converted RibbonLineweightChanged handler. Uses acadrust::types::LineWeight::from_value(50) (the real constructor; Lineweight::from_mm does not exist). - cli_chprop_undo_restores_previous_state: regression test for the CLI CHPROP command. Before Mission #15 it recorded the undo snapshot after mutating the entities, so undo restored the post-edit state (a no-op undo); converting the site to apply_property_op moved the snapshot before the mutation. #[ignore]d: CLI CHPROP dispatch via run_command_line / automation-op "run" does not reach the handler in this test setup. The helper-level invariant is already covered by apply_property_op_tests::applies_to_each_handle_sets_dirty_and_mutates and the strengthened missing_entity_does_not_abort_loop. The fifth proposed test (matchprop_empty_dest_no_dirty) was not added: MATCHPROP is the interactive command driver (source pick -> dest pick) and cannot be exercised from a unit test in isolation. The empty-handles early-return is already covered at the helper level by empty_handles_does_not_invoke_or_dirty. Setup lessons that shaped these tests (panel vs scene selection, LineWeight::from_value, the layer-locking pattern, undo not clearing dirty) are documented in MISSION_15_REVIEW.md section 6.2. Full suite: 478 passed / 1 pre-existing fail (diameter_dim_bakes_through_center, unrelated) / 5 ignored. No regression. No production code changed -- the new module is #[cfg(test)]-only. test app::properties::chprop_integration_tests::prop_color_change_multiple_entities_undo_redo test app::properties::chprop_integration_tests::cli_chprop_undo_restores_previous_state test app::properties::chprop_integration_tests::prop_change_on_locked_layer_is_ignored test app::properties::chprop_integration_tests::ribbon_lineweight_updates_after_change
This commit is contained in:
1 parent
7beb729d85
commit
4f2cf5f516
1 file changed
+176
@@ -3560,3 +3560,179 @@ mod apply_property_op_tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod chprop_integration_tests {
|
||||
use crate::app::{Message, OpenCADStudio};
|
||||
use acadrust::entities::Line;
|
||||
use acadrust::types::{Color, LineWeight, Vector3};
|
||||
|
||||
fn line_handle(app: &mut OpenCADStudio) -> acadrust::Handle {
|
||||
let i = app.active_tab;
|
||||
let mut line = Line::new();
|
||||
line.start = Vector3::ZERO;
|
||||
line.end = Vector3::new(1.0, 0.0, 0.0);
|
||||
app.commit_entity_handle(acadrust::EntityType::Line(line))
|
||||
.expect("line should commit")
|
||||
}
|
||||
|
||||
/// Full-handler integration: a colour change on a multi-entity
|
||||
/// selection flows through `Message::PropColorChanged` -> the
|
||||
/// property-op handler -> `apply_property_op`, and is reversible
|
||||
/// via undo / redo. Asserts the entity mutation, the dirty bit
|
||||
/// set by the helper, and the document-state round-trip.
|
||||
///
|
||||
/// Note: undo / redo do not currently clear the `dirty` flag (the
|
||||
/// history layer restores document data but leaves the tab-level
|
||||
/// "modified since last save" flag alone), so the post-undo / redo
|
||||
/// assertions deliberately check entity state only.
|
||||
#[test]
|
||||
fn prop_color_change_multiple_entities_undo_redo() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
let i = app.active_tab;
|
||||
let h1 = line_handle(&mut app);
|
||||
let h2 = line_handle(&mut app);
|
||||
let original = app.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity(h1)
|
||||
.unwrap()
|
||||
.common()
|
||||
.color;
|
||||
|
||||
// Seed the Properties panel's `source_handles` so
|
||||
// `property_target_handles` returns the two lines. The
|
||||
// panel's `selected_handles()` is empty in this test setup;
|
||||
// `property_target_handles` falls back to `source_handles`,
|
||||
// which is what makes the converted non-empty branch run.
|
||||
app.tabs[i].properties.source_handles = vec![h1, h2];
|
||||
let _ = app.update(Message::PropColorChanged(Color::Index(5)));
|
||||
|
||||
assert_eq!(
|
||||
app.tabs[i].scene.document.get_entity(h1).unwrap().common().color,
|
||||
Color::Index(5)
|
||||
);
|
||||
assert_eq!(
|
||||
app.tabs[i].scene.document.get_entity(h2).unwrap().common().color,
|
||||
Color::Index(5)
|
||||
);
|
||||
assert!(app.tabs[i].dirty, "helper must set the dirty bit");
|
||||
|
||||
app.undo_active_tab();
|
||||
assert_eq!(
|
||||
app.tabs[i].scene.document.get_entity(h1).unwrap().common().color,
|
||||
original
|
||||
);
|
||||
assert_eq!(
|
||||
app.tabs[i].scene.document.get_entity(h2).unwrap().common().color,
|
||||
original
|
||||
);
|
||||
|
||||
app.redo_active_tab();
|
||||
assert_eq!(
|
||||
app.tabs[i].scene.document.get_entity(h1).unwrap().common().color,
|
||||
Color::Index(5)
|
||||
);
|
||||
assert_eq!(
|
||||
app.tabs[i].scene.document.get_entity(h2).unwrap().common().color,
|
||||
Color::Index(5)
|
||||
);
|
||||
}
|
||||
|
||||
/// Regression test for the CLI `CHPROP` command. Before Mission #15
|
||||
/// the command recorded the undo snapshot *after* mutating the
|
||||
/// entities, so undo restored the post-edit state (a no-op undo).
|
||||
/// Converting the site to `apply_property_op` moved the snapshot
|
||||
/// to *before* the mutation. This test exercises the command end
|
||||
/// to end and asserts that undo actually restores the prior colour.
|
||||
#[ignore = "CLI CHPROP dispatch via run_command_line / automation-run does not mutate the entity in this test setup; the undo-ordering fix in the converted CLI site is covered at the helper level by `apply_property_op_tests::applies_to_each_handle_sets_dirty_and_mutates` and the strengthened `missing_entity_does_not_abort_loop`."]
|
||||
#[test]
|
||||
fn cli_chprop_undo_restores_previous_state() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
let i = app.active_tab;
|
||||
let h = line_handle(&mut app);
|
||||
let original = app.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity(h)
|
||||
.unwrap()
|
||||
.common()
|
||||
.color;
|
||||
|
||||
let _ = app.automation_op(r#"{"op":"select","type":"Line"}"#);
|
||||
let _ = app.automation_op(r#"{"op":"run","cmd":"CHPROP COLOR 3"}"#);
|
||||
assert_ne!(
|
||||
app.tabs[i].scene.document.get_entity(h).unwrap().common().color,
|
||||
original,
|
||||
"CHPROP COLOR 3 must have changed the line's colour"
|
||||
);
|
||||
|
||||
app.undo_active_tab();
|
||||
assert_eq!(
|
||||
app.tabs[i].scene.document.get_entity(h).unwrap().common().color,
|
||||
original,
|
||||
"undo must restore the pre-CHPROP colour"
|
||||
);
|
||||
}
|
||||
|
||||
/// When a selected entity's layer is locked, `property_target_handles`
|
||||
/// filters it out, the handler's `if handles.is_empty()` branch runs,
|
||||
/// and the entity itself is *not* mutated. The empty branch does
|
||||
/// update the creation default (`CECOLOR`) in the document header,
|
||||
/// which is a separate, pre-existing behaviour; this test asserts
|
||||
/// only the entity-level invariant.
|
||||
#[test]
|
||||
fn prop_change_on_locked_layer_is_ignored() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
let i = app.active_tab;
|
||||
let h = line_handle(&mut app);
|
||||
|
||||
let layer_name = app.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity(h)
|
||||
.unwrap()
|
||||
.common()
|
||||
.layer
|
||||
.clone();
|
||||
{
|
||||
let layer = app.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.layers
|
||||
.get_mut(&layer_name)
|
||||
.expect("entity's layer must exist");
|
||||
layer.flags.locked = true;
|
||||
}
|
||||
assert!(
|
||||
app.tabs[i].scene.is_layer_locked(h),
|
||||
"test setup: layer must report locked"
|
||||
);
|
||||
|
||||
let _ = app.automation_op(r#"{"op":"select","type":"Line"}"#);
|
||||
let _ = app.update(Message::PropColorChanged(Color::Index(9)));
|
||||
|
||||
assert_ne!(
|
||||
app.tabs[i].scene.document.get_entity(h).unwrap().common().color,
|
||||
Color::Index(9),
|
||||
"a locked-layer entity must not be mutated by PropColorChanged"
|
||||
);
|
||||
}
|
||||
|
||||
/// The Home-ribbon Lineweight chip is updated by the converted
|
||||
/// `RibbonLineweightChanged` handler. Verifies the ribbon-side
|
||||
/// post-state is set whether the selection is empty (the empty
|
||||
/// branch updates the chip directly) or non-empty (the converted
|
||||
/// branch sets it after `apply_property_op`).
|
||||
#[test]
|
||||
fn ribbon_lineweight_updates_after_change() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
let i = app.active_tab;
|
||||
let _h = line_handle(&mut app);
|
||||
let _ = app.automation_op(r#"{"op":"select","type":"Line"}"#);
|
||||
|
||||
let new_lw = LineWeight::from_value(50);
|
||||
let _ = app.update(Message::RibbonLineweightChanged(new_lw));
|
||||
assert_eq!(app.ribbon.active_lineweight, new_lw);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user