Files
OneCAD-Draft/tests/sketch_constraints_xrecord_roundtrip.rs
T

160 lines
5.8 KiB
Rust

// Proves a SketchConstraintSet serialized into an XRecord entry
// block record's extension dictionary, survives a real save/reload through
// both supported file formats byte-for-byte.
use acadrust::entities::EntityType;
use acadrust::objects::{XRecordEntry, XRecordValue};
use acadrust::types::{Handle, Vector3};
use OpenCADStudio::scene::named_parameters::DrivingValue;
use OpenCADStudio::scene::sketch_constraints::{
ConstraintKind, SketchConstraintSet, SketchRef, SketchScope,
};
use OpenCADStudio::scene::Scene;
const RECORD_KEY: &str = "OCS_SKETCH_CONSTRAINTS_TEST";
fn sample_set(scope: SketchScope) -> SketchConstraintSet {
let mut set = SketchConstraintSet::new(scope);
set.add(
ConstraintKind::Coincident,
vec![
SketchRef::point(Handle::new(101), 0),
SketchRef::point(Handle::new(102), 1),
],
None,
);
set.add(
ConstraintKind::Distance,
vec![SketchRef::whole(Handle::new(103))],
Some(DrivingValue::Literal(25.0)),
);
set.add(
ConstraintKind::Angle,
vec![
SketchRef::whole(Handle::new(104)),
SketchRef::whole(Handle::new(105)),
],
Some(DrivingValue::Literal(45.0)),
);
set.add(
ConstraintKind::Radius,
vec![SketchRef::whole(Handle::new(106))],
Some(DrivingValue::Literal(3.5)),
);
set
}
/// Writes `set` into a fresh XRecord Chunk under `owner`'s extension
/// dictionary, round-trips the whole document through `ext` (`"dxf"` or
/// anything else for DWG), and returns the `SketchConstraintSet` read back
/// out of the reloaded document.
fn roundtrip_through(
scene: &Scene,
owner: Handle,
set: &SketchConstraintSet,
ext: &str,
) -> SketchConstraintSet {
let mut doc = scene.document.clone();
let bytes = bincode::serialize(set).expect("serialize SketchConstraintSet");
doc.ensure_xrecord(owner, RECORD_KEY);
let record = doc
.xrecord_mut(owner, RECORD_KEY)
.expect("xrecord_mut after ensure_xrecord");
record
.entries
.push(XRecordEntry::new(310, XRecordValue::Chunk(bytes)));
let saved = OpenCADStudio::io::save_to_bytes(&doc, ext, doc.version)
.unwrap_or_else(|e| panic!("save to {ext} bytes: {e}"));
let reloaded = OpenCADStudio::io::load_bytes(&format!("roundtrip.{ext}"), saved)
.unwrap_or_else(|e| panic!("reload {ext} bytes: {e}"));
let record = reloaded
.xrecord(owner, RECORD_KEY)
.unwrap_or_else(|| panic!("XRecord {RECORD_KEY:?} missing after {ext} round-trip"));
let chunk = record
.entries
.iter()
.find_map(|entry| match &entry.value {
XRecordValue::Chunk(bytes) => Some(bytes.clone()),
_ => None,
})
.unwrap_or_else(|| panic!("no Chunk entry survived the {ext} round-trip"));
bincode::deserialize::<SketchConstraintSet>(&chunk)
.unwrap_or_else(|e| panic!("deserialize round-tripped {ext} blob: {e}"))
}
#[test]
fn sketch_constraint_set_survives_a_dxf_roundtrip() {
let scene = Scene::new();
let owner = scene.document.header.model_space_block_handle;
assert!(
!owner.is_null(),
"a fresh document must already have a model-space block handle"
);
let original = sample_set(SketchScope::ModelSpace);
let restored = roundtrip_through(&scene, owner, &original, "dxf");
assert_eq!(restored.scope, original.scope);
assert_eq!(restored.constraints.len(), original.constraints.len());
for (r, o) in restored.constraints.iter().zip(original.constraints.iter()) {
assert_eq!(r.id, o.id);
assert_eq!(r.kind, o.kind);
assert_eq!(r.refs, o.refs);
assert_eq!(r.driving_param, o.driving_param);
assert_eq!(r.enabled, o.enabled);
}
}
#[test]
fn sketch_constraint_set_survives_a_dwg_roundtrip() {
let scene = Scene::new();
let owner = scene.document.header.model_space_block_handle;
let original = sample_set(SketchScope::ModelSpace);
let restored = roundtrip_through(&scene, owner, &original, "dwg");
assert_eq!(restored.scope, original.scope);
assert_eq!(restored.constraints.len(), original.constraints.len());
for (r, o) in restored.constraints.iter().zip(original.constraints.iter()) {
assert_eq!(r.id, o.id);
assert_eq!(r.kind, o.kind);
assert_eq!(r.refs, o.refs);
assert_eq!(r.driving_param, o.driving_param);
}
}
/// A block-record-scoped set (not model space) — confirms the XRecord
/// extension-dictionary mechanism works for an arbitrary owner handle, not
/// just the one every document already has.
#[test]
fn sketch_constraint_set_survives_roundtrip_when_owned_by_a_block_record() {
let mut scene = Scene::new();
// A real block definition via the app's own `create_block_from_entities`
// (needs at least one source entity) — a bare, entity-less BlockRecord
// table entry turned out not to survive a DXF round-trip at all (the
// writer prunes it as unreferenced), so this exercises the actual shape
// `SketchScope::Block` will see in practice.
let line_handle = scene.add_entity(EntityType::Line(acadrust::entities::Line::from_points(
Vector3::new(0.0, 0.0, 0.0),
Vector3::new(1.0, 0.0, 0.0),
)));
let br_handle = scene
.create_block_from_entities(
&[line_handle],
"OCS_TEST_BLOCK",
&acadrust::types::Transform::identity(),
&acadrust::types::Transform::identity(),
)
.expect("create a block definition to own the constraint set");
let original = sample_set(SketchScope::Block(br_handle));
let restored = roundtrip_through(&scene, br_handle, &original, "dxf");
assert_eq!(restored.scope, SketchScope::Block(br_handle));
assert_eq!(restored.constraints.len(), original.constraints.len());
}