cad-editor/tests/active_space_roundtrip.rs
Hakan Seven 8b133ceab2 fix(layout): create the CTAB variable so the exact paper tab round-trips
Saving from a non-first paper layout still reopened on the first paper
tab: set_saved_active_layout only updated an existing CTAB entry, but
documents authored here never carried one, so CTAB was never written and
the reader fell back to $TILEMODE (which only records model-vs-paper) →
the first paper layout.

set_saved_active_layout now creates the CTAB DICTIONARYVAR under the root
named-object dictionary when it is absent (updating in place otherwise).
The root dictionary is taken from the header handle, or found by scanning
for the dictionary that holds ACAD_LAYOUT when a from-scratch document
has not populated the header handle yet. The writers persist it (they
serialize the document's objects and root-dictionary entries; the
root-dict rebuild in CadDocument::build() is not run on save).

Adds tests/active_space_roundtrip.rs, including a full DXF save→reload
round-trip asserting both $TILEMODE and CTAB survive.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 01:17:26 +03:00

79 lines
2.8 KiB
Rust

// The active space (Model vs a paper layout) a drawing was saved in must
// round-trip: `set_current_layout` mirrors it into the document as $TILEMODE
// (`header.show_model_space`) and the CTAB current-tab variable, and the loader
// restores it. Regression guard for the "always reopens in Model / the first
// paper layout" bugs.
use OpenCADStudio::scene::Scene;
#[test]
fn switching_to_paper_records_tilemode_and_ctab() {
let mut scene = Scene::new();
// A paper layout is active → TILEMODE says paper, CTAB names the tab.
scene.set_current_layout("Layout1".to_string());
assert!(
!scene.document.header.show_model_space,
"$TILEMODE should record paper space when a layout is active"
);
assert_eq!(
OpenCADStudio::io::saved_active_layout(&scene.document).as_deref(),
Some("Layout1"),
"CTAB must be created/updated so the exact paper tab round-trips (not \
just the first paper layout)"
);
// Back to Model → TILEMODE flips, CTAB follows.
scene.set_current_layout("Model".to_string());
assert!(
scene.document.header.show_model_space,
"$TILEMODE should record model space in the Model tab"
);
assert_eq!(
OpenCADStudio::io::saved_active_layout(&scene.document).as_deref(),
Some("Model"),
);
}
#[test]
fn active_paper_layout_survives_a_dxf_save_and_reload() {
let mut scene = Scene::new();
scene.set_current_layout("Layout1".to_string());
// Full file round-trip: write to DXF bytes, read them back.
let bytes = OpenCADStudio::io::save_to_bytes(&scene.document, "dxf", scene.document.version)
.expect("save to DXF bytes");
let doc = OpenCADStudio::io::load_bytes("roundtrip.dxf", bytes).expect("reload DXF bytes");
assert!(
!doc.header.show_model_space,
"$TILEMODE must persist paper space across a DXF save/reload"
);
assert_eq!(
OpenCADStudio::io::saved_active_layout(&doc).as_deref(),
Some("Layout1"),
"CTAB must persist the exact active tab across a DXF save/reload"
);
}
#[test]
fn ctab_is_created_when_absent_then_updated_in_place() {
let mut scene = Scene::new();
let doc = &mut scene.document;
// A brand-new document carries no CTAB entry.
assert_eq!(OpenCADStudio::io::saved_active_layout(doc), None);
// First write creates it; a second write must update in place, not stack a
// duplicate entry that a reader could resolve to the stale value.
OpenCADStudio::io::set_saved_active_layout(doc, "Layout2");
assert_eq!(
OpenCADStudio::io::saved_active_layout(doc).as_deref(),
Some("Layout2")
);
OpenCADStudio::io::set_saved_active_layout(doc, "Layout3");
assert_eq!(
OpenCADStudio::io::saved_active_layout(doc).as_deref(),
Some("Layout3")
);
}