Selecting a whole drawing was unusable on a 186 468-entity file. Four costs
scaled with the selection where they did not have to.
Every selected handle triggered a walk of the entire document to find LEADERs
pointing at it. On the file this came from, selecting 471 583 entities in a
585 786-entity drawing did not finish inside eight minutes. A reverse index
built once per geometry epoch answers the same question; a test asserts it
returns exactly what the walk returned, including the case of two leaders on
one annotation.
Group expansion asked the selection about every group: for each selected
handle it scanned each group's own member list. Asking each group once
whether it holds anything selected turns that around.
Selecting and deselecting went one entity at a time, and `deselect_entity`
rebuilt the ordered list with a `retain` per handle, so removing N of M
walked the order list N times. Both now take a slice.
Grips were built for every selected entity. AutoCAD stops at GRIPOBJLIMIT,
and the DWG header does not carry the variable, so its default of 100 stands
in for it.
The remaining two commits are measurement: `select-commit` reports the phases
of a selection separately, which is what showed the bulk paths had been added
to the press-drag branch while a click-move-click window goes through a
different one entirely.
Reuse the application library from the binary entry point instead of compiling the module tree twice. Keep desktop-only transports and caches out of web builds, remove unused test code, and check executable targets in CI.
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
Extract the push-undo → per-handle → invalidate → dirty → refresh
contract into one helper on OpenCADStudio. The helper is the single
source of truth for property-op side effects, so future property
handlers can't forget the cache invalidation or the panel refresh.
Note: MATCHPROP now no-ops when the destination selection is empty
(previously it marked the tab dirty and refreshed the panel). This is
a minor correctness improvement; restore the old behaviour with a
2-line guard at the MATCHPROP call site if desired.
test app::properties::apply_property_op_tests::empty_handles_does_not_invoke_or_dirty
test app::properties::apply_property_op_tests::applies_to_each_handle_sets_dirty_and_mutates
test app::properties::apply_property_op_tests::missing_entity_does_not_abort_loop
Sync current main and companion codec changes before preserving unsupported frame rows, rendering in the entity plane, and sharing final layout metrics with placement preview.