diff --git a/src/entities/attdef.rs b/src/entities/attdef.rs index fbe9ba5..e81089f 100644 --- a/src/entities/attdef.rs +++ b/src/entities/attdef.rs @@ -161,68 +161,3 @@ fn read_tv(c: &mut BitCursor<'_>, version: Version) -> Result { Ok(crate::code_page::decode_active(&bytes)) } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - - #[test] - fn r2007_split_stream_attdef_reads_value_tag_and_prompt() { - let mut body = BitWriter::new(); - text::tests::write_r2018_preamble(&mut body); - body.write_rc(0xFF); - body.write_rd(0.0); - body.write_rd(0.0); - body.write_b(true); // extrusion default - body.write_b(true); // thickness zero - body.write_rd(2.5); // height - body.write_rc(0); // ATTRIB's R2010+ version byte - body.write_rc(1); // R2018+ attribute type = single line - body.write_bs(0); // field length - body.write_rc(0x00); // flags - body.write_b(false); // lock position - body.write_rc(0); // ATTDEF's own R2010+ version byte - let bits = crate::string_stream::tests::bits_of(&body); - let payload = - crate::string_stream::tests::build_payload(&bits, &["1", "ATTINFO", "Enter number:"]); - let a = decode_modern_split_stream(&payload, 8, Version::R2018).unwrap(); - assert_eq!(a.tag, "ATTINFO"); - assert_eq!(a.prompt, "Enter number:"); - assert_eq!(a.text.text, "1"); - assert_eq!(a.text.height, 2.5); - assert_eq!(a.attribute_type, 1); - assert!(a.mtext.is_none()); - } - - #[test] - fn roundtrip_attdef_r2000() { - let mut w = BitWriter::new(); - // TEXT preamble — minimal - w.write_rc(0xFF); - w.write_rd(0.0); - w.write_rd(0.0); - w.write_b(true); - w.write_b(true); - w.write_rd(2.5); - w.write_bs_u(0); // empty default text - // ATTRIB fields - w.write_bs_u(5); // tag - for b in b"PRICE" { - w.write_rc(*b); - } - w.write_bs(0); // field length - w.write_rc(0x00); // flags - // ATTDEF's own prompt, last - w.write_bs_u(14); // prompt - for b in b"Enter price: " { - w.write_rc(*b); - } - w.write_rc(0); // trailing NUL (stripped) - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let a = decode(&mut c, Version::R2000).unwrap(); - assert_eq!(a.prompt, "Enter price: "); - assert_eq!(a.tag, "PRICE"); - } -} diff --git a/src/entities/attrib.rs b/src/entities/attrib.rs index 3439b14..4c0aafb 100644 --- a/src/entities/attrib.rs +++ b/src/entities/attrib.rs @@ -250,145 +250,3 @@ fn read_tv(c: &mut BitCursor<'_>, version: Version) -> Result { Ok(crate::code_page::decode_active(&bytes)) } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - - #[test] - fn r2007_split_stream_attrib_reads_value_and_tag() { - let mut body = BitWriter::new(); - text::tests::write_r2018_preamble(&mut body); - body.write_rc(0xFF); - body.write_rd(0.0); - body.write_rd(0.0); - body.write_b(true); // extrusion default - body.write_b(true); // thickness zero - body.write_rd(2.5); // height - body.write_rc(0); // R2010+ version byte - body.write_rc(1); // R2018+ attribute type = single line - body.write_bs(0); // field length - body.write_rc(0x01); // flags — invisible - body.write_b(false); // lock position - let bits = crate::string_stream::tests::bits_of(&body); - let payload = crate::string_stream::tests::build_payload(&bits, &["17", "ATTINFO"]); - let a = decode_modern_split_stream(&payload, 8, Version::R2018).unwrap(); - assert_eq!(a.tag, "ATTINFO"); - assert_eq!(a.text.text, "17"); - assert_eq!(a.text.height, 2.5); - assert_eq!(a.attribute_type, 1); - assert!(a.mtext.is_none()); - assert!(a.is_invisible()); - assert!(!a.lock_position); - } - - /// The R2018 multi-line shape measured on `sample_AC1032.dwg` - /// handle `0x79D`: attribute type `2`, then a whole MTEXT record - /// embedded from its entity-mode bits on, then the annotative-data - /// size and the attribute's own flags. Its three strings are the - /// (empty) TEXT value, the embedded MTEXT's text, and the tag. - #[test] - fn r2018_multi_line_attrib_embeds_an_mtext() { - let mut body = BitWriter::new(); - text::tests::write_r2018_preamble(&mut body); - body.write_rc(0xFF); // TEXT data flags: nothing optional present - body.write_rd(977.28); // insertion x - body.write_rd(27.03); // insertion y - body.write_b(true); // extrusion default - body.write_b(true); // thickness zero - body.write_rd(1.72); // height - body.write_rc(0); // R2010+ version byte - body.write_rc(2); // R2018+ attribute type = multi-line ATTRIB - // Embedded MTEXT: the common entity preamble from entmode on. - body.write_bb(0b00); // entmode - body.write_bl(0); // num reactors - body.write_b(true); // no xdictionary - body.write_b(false); // no AcDs binary data - body.write_bs(0); // colour - body.write_bd(1.0); // linetype scale - body.write_bb(0b00); - body.write_bb(0b00); - body.write_bb(0b00); - body.write_rc(0); - body.write_b(false); - body.write_b(false); - body.write_b(false); - body.write_bs(0); // invisibility - body.write_rc(0x1D); // lineweight - // Embedded MTEXT field body. - for v in [977.28, 28.75, 0.0] { - body.write_bd(v); - } - for v in [0.0, 0.0, 1.0] { - body.write_bd(v); - } - for v in [1.0, 0.0, 0.0] { - body.write_bd(v); - } - body.write_bd(0.0); // rect width - body.write_bd(0.0); // rect height - body.write_bd(1.72); // nominal text height - body.write_bs(1); // attachment point - body.write_bs(5); // drawing direction - body.write_bd(1.84); // extents height - body.write_bd(16.37); // extents width - body.write_bs(1); // linespace style - body.write_bd(1.0); // linespace factor - body.write_b(false); // unknown bit - body.write_bl(0); // background flags - body.write_b(false); // R2018 "is NOT annotative" — clear - // Back in the ATTRIB. - body.write_bs(0); // annotative data size - body.write_bs(0); // unknown 73 - body.write_rc(0x00); // flags - body.write_b(true); // lock position - - let bits = crate::string_stream::tests::bits_of(&body); - let payload = crate::string_stream::tests::build_payload( - &bits, - &["", "my multi line text for the attrrib", "MULTI_LINE_ATT"], - ); - let a = decode_modern_split_stream(&payload, 8, Version::R2018).unwrap(); - assert_eq!(a.attribute_type, 2); - assert_eq!(a.tag, "MULTI_LINE_ATT"); - assert_eq!(a.text.text, ""); - let m = a.mtext.expect("embedded MTEXT"); - assert_eq!(m.text, "my multi line text for the attrrib"); - assert_eq!(m.extents_width, 16.37); - assert!(a.lock_position); - } - - #[test] - fn roundtrip_invisible_constant_attrib() { - let mut w = BitWriter::new(); - // minimal TEXT payload - w.write_rc(0xFF); - w.write_rd(0.0); - w.write_rd(0.0); - w.write_b(true); // ext default - w.write_b(true); // thickness default - w.write_rd(2.5); - w.write_bs_u(3); // "ABC" - w.write_rc(b'A'); - w.write_rc(b'B'); - w.write_rc(b'C'); - // attrib-specific - w.write_bs_u(5); // tag "PRICE" - w.write_rc(b'P'); - w.write_rc(b'R'); - w.write_rc(b'I'); - w.write_rc(b'C'); - w.write_rc(b'E'); - w.write_bs(0); // field_length - w.write_rc(0x03); // invisible + constant - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let a = decode(&mut c, Version::R2000).unwrap(); - assert_eq!(a.tag, "PRICE"); - assert_eq!(a.text.text, "ABC"); - assert!(a.is_invisible()); - assert!(a.is_constant()); - assert!(!a.is_verifiable()); - } -} diff --git a/src/entities/block.rs b/src/entities/block.rs index 98b3e0f..4a81e5f 100644 --- a/src/entities/block.rs +++ b/src/entities/block.rs @@ -85,23 +85,3 @@ pub fn decode(c: &mut BitCursor<'_>, version: Version) -> Result { }) } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - - #[test] - fn roundtrip_modelspace_block_r2000() { - let mut w = BitWriter::new(); - let s = b"*Model_Space"; - w.write_bs_u(s.len() as u16); - for b in s { - w.write_rc(*b); - } - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let b = decode(&mut c, Version::R2000).unwrap(); - assert_eq!(b.name, "*Model_Space"); - } -} diff --git a/src/entities/body.rs b/src/entities/body.rs index e9c7cd6..99af266 100644 --- a/src/entities/body.rs +++ b/src/entities/body.rs @@ -63,73 +63,3 @@ pub fn decode_record(c: &mut BitCursor<'_>, version: Version, in_data_store: boo tail: Some(record.tail), }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - - #[test] - fn roundtrip_empty_body_entity() { - let mut w = BitWriter::new(); - w.write_b(true); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let b = decode(&mut c).unwrap(); - assert!(b.acis_empty); - assert_eq!(b.version, None); - assert_eq!(b.sat_blob, None); - } - - #[test] - fn roundtrip_body_with_blob() { - let payload = b"BODY_SAT_PAYLOAD_BYTES"; - let mut w = BitWriter::new(); - w.write_b(false); - w.write_b(false); // §20.4.41 unknown bit - w.write_bs_u(1); - w.write_bl(payload.len() as i32); - for x in payload { - w.write_rc(*x); - } - w.write_bl(0); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let b = decode(&mut c).unwrap(); - assert!(!b.acis_empty); - assert_eq!(b.version, Some(1)); - assert_eq!(b.sat_blob.as_deref(), Some(&payload[..])); - } - - /// The R2013+ data-store shape: no inline envelope, a wireframe - /// point, `num isolines = 4` and the 16-byte revision GUID. - #[test] - fn roundtrip_body_data_store_record() { - let guid = [0xCDu8; 16]; - let mut w = BitWriter::new(); - w.write_b(true); // wireframe data present - w.write_b(false); // point present - w.write_bl(4); // num isolines - w.write_b(false); // isolines present - w.write_b(true); // ACIS empty (2) - w.write_bl(0); // R2007+ unknown - w.write_b(true); - w.write_bb(0); - w.write_bb(0); - w.write_bb(0b10); - for b in guid { - w.write_rc(b); - } - w.write_bl(0); - let end = w.position_bits(); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let b = decode_record(&mut c, Version::R2018, true).unwrap(); - assert!(b.in_data_store); - let fields = b.tail.unwrap(); - assert_eq!(fields.num_isolines, 4); - assert!(!fields.isolines_present); - assert_eq!(fields.revision_guid, Some(guid)); - assert_eq!(c.position_bits(), end); - } -} diff --git a/src/entities/camera.rs b/src/entities/camera.rs index 5e621c6..8bf085b 100644 --- a/src/entities/camera.rs +++ b/src/entities/camera.rs @@ -65,64 +65,3 @@ pub fn decode(c: &mut BitCursor<'_>, version: Version) -> Result { view_handle, }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - - #[test] - fn roundtrip_camera_default_focal() { - let mut w = BitWriter::new(); - // origin (1.0, 2.0, 3.0) - w.write_bd(1.0); - w.write_bd(2.0); - w.write_bd(3.0); - // direction (0.0, 0.0, -1.0) - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(-1.0); - // focal length — BD 1.0 short-form represents 1.0, so use an - // explicit double for 50.0mm. - w.write_bd(50.0); - // FoV ≈ 0.6 rad (about 34° full-angle, normal lens) - w.write_bd(0.6); - // view handle 5.1.0x42 - w.write_handle(5, 0x42); - - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let cam = decode(&mut c, Version::R2010).unwrap(); - assert_eq!( - cam.origin, - Point3D { - x: 1.0, - y: 2.0, - z: 3.0 - } - ); - assert_eq!( - cam.direction, - Vec3D { - x: 0.0, - y: 0.0, - z: -1.0 - } - ); - assert_eq!(cam.focal_length, 50.0); - assert!((cam.field_of_view - 0.6).abs() < 1e-12); - assert_eq!(cam.view_handle.code, 5); - assert_eq!(cam.view_handle.value, 0x42); - } - - #[test] - fn rejects_pre_r2007() { - let bytes = [0u8; 4]; - let mut c = BitCursor::new(&bytes); - let err = decode(&mut c, Version::R2004).unwrap_err(); - assert!( - matches!(&err, Error::Unsupported { feature } if feature.contains("CAMERA")), - "err={err:?}" - ); - } -} diff --git a/src/entities/dimension_aligned.rs b/src/entities/dimension_aligned.rs index 1300819..b9250d8 100644 --- a/src/entities/dimension_aligned.rs +++ b/src/entities/dimension_aligned.rs @@ -61,108 +61,3 @@ pub fn decode( }, }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - use crate::entities::dimension::read_common; - - fn write_common(w: &mut BitWriter, version: Version) { - if version.is_r2010_plus() { - w.write_rc(0); - } - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_rd(5.0); - w.write_rd(5.0); - w.write_bd(0.0); - w.write_rc(0x00); - w.write_bs_u(0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(1.0); - w.write_bd(1.0); - w.write_bd(0.0); - w.write_bs(0); - w.write_bs(0); - w.write_bd(1.0); - w.write_bd(10.0); - if version.is_r2007_plus() { - w.write_b(false); - w.write_b(false); - w.write_b(false); - } - w.write_rd(0.0); - w.write_rd(0.0); - } - - #[test] - fn roundtrip_aligned_r2000_no_oblique() { - let mut w = BitWriter::new(); - write_common(&mut w, Version::R2000); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(5.0); - w.write_bd(5.0); - w.write_bd(0.0); - w.write_bd(2.5); - w.write_bd(2.5); - w.write_bd(0.0); - w.write_bd(0.0); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let common = read_common(&mut c, Version::R2000).unwrap(); - let d = decode(&mut c, &common, Version::R2000).unwrap(); - assert_eq!(d.suffix.extension_line_2_origin.x, 5.0); - assert_eq!(d.suffix.extension_line_2_origin.y, 5.0); - assert_eq!(d.suffix.oblique_angle_radians, 0.0); - } - - #[test] - fn roundtrip_aligned_with_oblique() { - let mut w = BitWriter::new(); - write_common(&mut w, Version::R2000); - w.write_bd(1.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(7.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(4.0); - w.write_bd(3.0); - w.write_bd(0.0); - w.write_bd(std::f64::consts::FRAC_PI_3); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let common = read_common(&mut c, Version::R2000).unwrap(); - let d = decode(&mut c, &common, Version::R2000).unwrap(); - assert!((d.suffix.oblique_angle_radians - std::f64::consts::FRAC_PI_3).abs() < 1e-12); - assert_eq!(d.suffix.dim_line_definition_point.y, 3.0); - } - - #[test] - fn roundtrip_aligned_r2007_plus() { - let mut w = BitWriter::new(); - write_common(&mut w, Version::R2007); - w.write_bd(2.0); - w.write_bd(2.0); - w.write_bd(0.0); - w.write_bd(12.0); - w.write_bd(2.0); - w.write_bd(0.0); - w.write_bd(7.0); - w.write_bd(5.0); - w.write_bd(0.0); - w.write_bd(0.25); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let common = read_common(&mut c, Version::R2007).unwrap(); - let d = decode(&mut c, &common, Version::R2007).unwrap(); - assert_eq!(d.suffix.extension_line_1_origin.x, 2.0); - assert_eq!(d.suffix.oblique_angle_radians, 0.25); - } -} diff --git a/src/entities/dimension_angular_2l.rs b/src/entities/dimension_angular_2l.rs index 4c6d824..ee94619 100644 --- a/src/entities/dimension_angular_2l.rs +++ b/src/entities/dimension_angular_2l.rs @@ -56,98 +56,3 @@ pub fn decode( }, }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - use crate::entities::dimension::read_common; - - fn write_common(w: &mut BitWriter, version: Version) { - if version.is_r2010_plus() { - w.write_rc(0); - } - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_rd(5.0); - w.write_rd(5.0); - w.write_bd(0.0); - w.write_rc(0x00); - w.write_bs_u(0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(1.0); - w.write_bd(1.0); - w.write_bd(0.0); - w.write_bs(0); - w.write_bs(0); - w.write_bd(1.0); - w.write_bd(10.0); - if version.is_r2007_plus() { - w.write_b(false); - w.write_b(false); - w.write_b(false); - } - w.write_rd(0.0); - w.write_rd(0.0); - } - - fn write_point(w: &mut BitWriter, p: (f64, f64, f64)) { - w.write_bd(p.0); - w.write_bd(p.1); - w.write_bd(p.2); - } - - #[test] - fn roundtrip_angular_2l_perpendicular_lines() { - // Line 1: (0,0) → (10,0) - // Line 2: (0,0) → (0,10) → perpendicular at the origin - let mut w = BitWriter::new(); - write_common(&mut w, Version::R2000); - write_point(&mut w, (0.0, 0.0, 0.0)); - write_point(&mut w, (10.0, 0.0, 0.0)); - write_point(&mut w, (0.0, 0.0, 0.0)); - write_point(&mut w, (0.0, 10.0, 0.0)); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let common = read_common(&mut c, Version::R2000).unwrap(); - let d = decode(&mut c, &common, Version::R2000).unwrap(); - assert_eq!(d.suffix.extension_line_1a.x, 0.0); - assert_eq!(d.suffix.extension_line_1b.x, 10.0); - assert_eq!(d.suffix.extension_line_2b.y, 10.0); - } - - #[test] - fn roundtrip_angular_2l_arbitrary() { - let mut w = BitWriter::new(); - write_common(&mut w, Version::R2000); - write_point(&mut w, (1.0, 2.0, 3.0)); - write_point(&mut w, (4.0, 5.0, 6.0)); - write_point(&mut w, (7.0, 8.0, 9.0)); - write_point(&mut w, (10.0, 11.0, 12.0)); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let common = read_common(&mut c, Version::R2000).unwrap(); - let d = decode(&mut c, &common, Version::R2000).unwrap(); - assert_eq!(d.suffix.extension_line_1a.x, 1.0); - assert_eq!(d.suffix.extension_line_2a.y, 8.0); - assert_eq!(d.suffix.extension_line_2b.z, 12.0); - } - - #[test] - fn roundtrip_angular_2l_r2007_plus() { - let mut w = BitWriter::new(); - write_common(&mut w, Version::R2007); - write_point(&mut w, (0.1, 0.2, 0.3)); - write_point(&mut w, (0.4, 0.5, 0.6)); - write_point(&mut w, (0.7, 0.8, 0.9)); - write_point(&mut w, (1.0, 1.1, 1.2)); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let common = read_common(&mut c, Version::R2007).unwrap(); - let d = decode(&mut c, &common, Version::R2007).unwrap(); - assert_eq!(d.suffix.extension_line_1b.y, 0.5); - } -} diff --git a/src/entities/dimension_angular_3p.rs b/src/entities/dimension_angular_3p.rs index 7e7c505..111c628 100644 --- a/src/entities/dimension_angular_3p.rs +++ b/src/entities/dimension_angular_3p.rs @@ -51,108 +51,3 @@ pub fn decode( }, }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - use crate::entities::dimension::read_common; - - fn write_common(w: &mut BitWriter, version: Version) { - if version.is_r2010_plus() { - w.write_rc(0); - } - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_rd(5.0); - w.write_rd(5.0); - w.write_bd(0.0); - w.write_rc(0x00); - w.write_bs_u(0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(1.0); - w.write_bd(1.0); - w.write_bd(0.0); - w.write_bs(0); - w.write_bs(0); - w.write_bd(1.0); - w.write_bd(10.0); - if version.is_r2007_plus() { - w.write_b(false); - w.write_b(false); - w.write_b(false); - } - w.write_rd(0.0); - w.write_rd(0.0); - } - - #[test] - fn roundtrip_angular_3p_right_angle() { - // Right angle at the origin between +x and +y axes. - let mut w = BitWriter::new(); - write_common(&mut w, Version::R2000); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); // center - w.write_bd(10.0); - w.write_bd(0.0); - w.write_bd(0.0); // first extension - w.write_bd(0.0); - w.write_bd(10.0); - w.write_bd(0.0); // second extension - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let common = read_common(&mut c, Version::R2000).unwrap(); - let d = decode(&mut c, &common, Version::R2000).unwrap(); - assert_eq!(d.suffix.center.x, 0.0); - assert_eq!(d.suffix.first_extension_point.x, 10.0); - assert_eq!(d.suffix.second_extension_point.y, 10.0); - } - - #[test] - fn roundtrip_angular_3p_offset_center() { - let mut w = BitWriter::new(); - write_common(&mut w, Version::R2000); - w.write_bd(5.0); - w.write_bd(5.0); - w.write_bd(0.0); - w.write_bd(15.0); - w.write_bd(5.0); - w.write_bd(0.0); - w.write_bd(5.0); - w.write_bd(15.0); - w.write_bd(0.0); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let common = read_common(&mut c, Version::R2000).unwrap(); - let d = decode(&mut c, &common, Version::R2000).unwrap(); - assert_eq!(d.suffix.center.x, 5.0); - assert_eq!(d.suffix.center.y, 5.0); - assert_eq!(d.suffix.first_extension_point.x, 15.0); - } - - #[test] - fn roundtrip_angular_3p_r2010() { - let mut w = BitWriter::new(); - write_common(&mut w, Version::R2010); - w.write_bd(1.0); - w.write_bd(2.0); - w.write_bd(3.0); - w.write_bd(4.0); - w.write_bd(5.0); - w.write_bd(6.0); - w.write_bd(7.0); - w.write_bd(8.0); - w.write_bd(9.0); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let common = read_common(&mut c, Version::R2010).unwrap(); - let d = decode(&mut c, &common, Version::R2010).unwrap(); - assert_eq!(d.suffix.center.z, 3.0); - assert_eq!(d.suffix.first_extension_point.y, 5.0); - assert_eq!(d.suffix.second_extension_point.x, 7.0); - } -} diff --git a/src/entities/dimension_diameter.rs b/src/entities/dimension_diameter.rs index fc8f1bf..0679fb1 100644 --- a/src/entities/dimension_diameter.rs +++ b/src/entities/dimension_diameter.rs @@ -18,73 +18,3 @@ pub use crate::entities::dimension_radial::{ DimensionDiameter, DimensionRadialSuffix, decode_diameter as decode, }; - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitcursor::BitCursor; - use crate::bitwriter::BitWriter; - use crate::entities::dimension::read_common; - use crate::version::Version; - - fn write_common(w: &mut BitWriter, version: Version) { - if version.is_r2010_plus() { - w.write_rc(0); - } - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_rd(5.0); - w.write_rd(5.0); - w.write_bd(0.0); - w.write_rc(0x00); - w.write_bs_u(0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(1.0); - w.write_bd(1.0); - w.write_bd(0.0); - w.write_bs(0); - w.write_bs(0); - w.write_bd(1.0); - w.write_bd(10.0); - if version.is_r2007_plus() { - w.write_b(false); - w.write_b(false); - w.write_b(false); - } - w.write_rd(0.0); - w.write_rd(0.0); - } - - #[test] - fn decode_via_reexport() { - let mut w = BitWriter::new(); - write_common(&mut w, Version::R2000); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(12.5); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(6.25); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let common = read_common(&mut c, Version::R2000).unwrap(); - let d: DimensionDiameter = decode(&mut c, &common, Version::R2000).unwrap(); - assert_eq!(d.suffix.first_arc_point.x, 12.5); - assert_eq!(d.suffix.leader_length, 6.25); - } - - #[test] - fn suffix_shape_matches_radial() { - // Sanity-check the re-export — the public type is the one - // declared in dimension_radial, not a duplicate. - let _s: DimensionRadialSuffix = DimensionRadialSuffix { - center_point: crate::entities::Point3D::default(), - first_arc_point: crate::entities::Point3D::default(), - leader_length: 0.0, - }; - } -} diff --git a/src/entities/dimension_linear.rs b/src/entities/dimension_linear.rs index 06cb7b6..583127e 100644 --- a/src/entities/dimension_linear.rs +++ b/src/entities/dimension_linear.rs @@ -79,113 +79,3 @@ pub fn decode( }, }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - use crate::entities::dimension::read_common; - - /// Write a minimal common dimension preamble matching the test - /// helper in `dimension.rs`. - fn write_common(w: &mut BitWriter, version: Version) { - if version.is_r2010_plus() { - w.write_rc(0); - } - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); // extrusion - w.write_rd(5.0); - w.write_rd(5.0); // text midpoint - w.write_bd(0.0); // elevation - w.write_rc(0x00); // flags - w.write_bs_u(0); // empty user text - w.write_bd(0.0); // text rotation - w.write_bd(0.0); // horiz dir - w.write_bd(1.0); - w.write_bd(1.0); - w.write_bd(1.0); // ins scale - w.write_bd(0.0); // ins rotation - w.write_bs(0); // attachment - w.write_bs(0); // line-spacing style - w.write_bd(1.0); // line-spacing factor - w.write_bd(10.0); // actual measurement - if version.is_r2007_plus() { - w.write_b(false); - w.write_b(false); - w.write_b(false); - } - w.write_rd(0.0); - w.write_rd(0.0); // def point 12 - } - - #[test] - fn roundtrip_linear_r2000_basic() { - let mut w = BitWriter::new(); - write_common(&mut w, Version::R2000); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); // ext line 1 - w.write_bd(10.0); - w.write_bd(0.0); - w.write_bd(0.0); // ext line 2 - w.write_bd(5.0); - w.write_bd(2.0); - w.write_bd(0.0); // dim line def point - w.write_bd(0.0); // rotation - w.write_bd(0.0); // oblique - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let common = read_common(&mut c, Version::R2000).unwrap(); - let d = decode(&mut c, &common, Version::R2000).unwrap(); - assert_eq!(d.suffix.extension_line_2_origin.x, 10.0); - assert_eq!(d.suffix.dim_line_definition_point.y, 2.0); - assert_eq!(d.suffix.rotation_radians, 0.0); - } - - #[test] - fn roundtrip_linear_r2000_rotated() { - let mut w = BitWriter::new(); - write_common(&mut w, Version::R2000); - w.write_bd(1.0); - w.write_bd(2.0); - w.write_bd(3.0); - w.write_bd(11.0); - w.write_bd(2.0); - w.write_bd(3.0); - w.write_bd(6.0); - w.write_bd(4.0); - w.write_bd(3.0); - w.write_bd(std::f64::consts::FRAC_PI_4); - w.write_bd(std::f64::consts::FRAC_PI_6); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let common = read_common(&mut c, Version::R2000).unwrap(); - let d = decode(&mut c, &common, Version::R2000).unwrap(); - assert!((d.suffix.rotation_radians - std::f64::consts::FRAC_PI_4).abs() < 1e-12); - assert!((d.suffix.oblique_angle_radians - std::f64::consts::FRAC_PI_6).abs() < 1e-12); - assert_eq!(d.suffix.extension_line_1_origin.x, 1.0); - } - - #[test] - fn roundtrip_linear_r2010_version_gate() { - let mut w = BitWriter::new(); - write_common(&mut w, Version::R2010); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.5); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let common = read_common(&mut c, Version::R2010).unwrap(); - let d = decode(&mut c, &common, Version::R2010).unwrap(); - assert_eq!(d.suffix.extension_line_2_origin.x, 1.0); - } -} diff --git a/src/entities/dimension_ordinate.rs b/src/entities/dimension_ordinate.rs index c642152..198a8f6 100644 --- a/src/entities/dimension_ordinate.rs +++ b/src/entities/dimension_ordinate.rs @@ -78,106 +78,3 @@ pub fn decode( }, }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - use crate::entities::dimension::read_common; - - fn write_common(w: &mut BitWriter, version: Version) { - if version.is_r2010_plus() { - w.write_rc(0); - } - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_rd(5.0); - w.write_rd(5.0); - w.write_bd(0.0); - w.write_rc(0x00); - w.write_bs_u(0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(1.0); - w.write_bd(1.0); - w.write_bd(0.0); - w.write_bs(0); - w.write_bs(0); - w.write_bd(1.0); - w.write_bd(10.0); - if version.is_r2007_plus() { - w.write_b(false); - w.write_b(false); - w.write_b(false); - } - w.write_rd(0.0); - w.write_rd(0.0); - } - - #[test] - fn roundtrip_ordinate_y_type() { - let mut w = BitWriter::new(); - write_common(&mut w, Version::R2000); - w.write_bd(3.0); - w.write_bd(4.0); - w.write_bd(0.0); // feature - w.write_bd(3.0); - w.write_bd(10.0); - w.write_bd(0.0); // leader - w.write_rc(0x00); // Y-type flag - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let common = read_common(&mut c, Version::R2000).unwrap(); - let d = decode(&mut c, &common, Version::R2000).unwrap(); - assert_eq!(d.suffix.feature_location.x, 3.0); - assert_eq!(d.suffix.feature_location.y, 4.0); - assert_eq!(d.suffix.leader_endpoint.y, 10.0); - assert_eq!(d.suffix.flags, 0x00); - assert_eq!(d.suffix.axis(), OrdinateAxis::Y); - } - - #[test] - fn roundtrip_ordinate_x_type() { - let mut w = BitWriter::new(); - write_common(&mut w, Version::R2000); - w.write_bd(5.0); - w.write_bd(2.0); - w.write_bd(0.0); - w.write_bd(12.0); - w.write_bd(2.0); - w.write_bd(0.0); - w.write_rc(0x01); // X-type flag - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let common = read_common(&mut c, Version::R2000).unwrap(); - let d = decode(&mut c, &common, Version::R2000).unwrap(); - assert_eq!(d.suffix.feature_location.x, 5.0); - assert_eq!(d.suffix.leader_endpoint.x, 12.0); - assert_eq!(d.suffix.flags, 0x01); - assert_eq!(d.suffix.axis(), OrdinateAxis::X); - } - - #[test] - fn ordinate_flag_reserved_bits_preserved() { - // Ensure higher-order flag bits survive round-trip — they - // are ODA-reserved, so we preserve them even though we don't - // interpret them. - let mut w = BitWriter::new(); - write_common(&mut w, Version::R2000); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_rc(0xF1); // x-type, plus high reserved bits - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let common = read_common(&mut c, Version::R2000).unwrap(); - let d = decode(&mut c, &common, Version::R2000).unwrap(); - assert_eq!(d.suffix.flags, 0xF1); - assert_eq!(d.suffix.axis(), OrdinateAxis::X); - } -} diff --git a/src/entities/dimension_radial.rs b/src/entities/dimension_radial.rs index 959f699..610245b 100644 --- a/src/entities/dimension_radial.rs +++ b/src/entities/dimension_radial.rs @@ -89,103 +89,3 @@ fn read_suffix(c: &mut BitCursor<'_>) -> Result { leader_length, }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - use crate::entities::dimension::read_common; - - fn write_common(w: &mut BitWriter, version: Version) { - if version.is_r2010_plus() { - w.write_rc(0); - } - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_rd(5.0); - w.write_rd(5.0); - w.write_bd(0.0); - w.write_rc(0x00); - w.write_bs_u(0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(1.0); - w.write_bd(1.0); - w.write_bd(0.0); - w.write_bs(0); - w.write_bs(0); - w.write_bd(1.0); - w.write_bd(10.0); - if version.is_r2007_plus() { - w.write_b(false); - w.write_b(false); - w.write_b(false); - } - w.write_rd(0.0); - w.write_rd(0.0); - } - - fn write_radial_suffix(w: &mut BitWriter, cp: (f64, f64, f64), fap: (f64, f64, f64), ll: f64) { - w.write_bd(cp.0); - w.write_bd(cp.1); - w.write_bd(cp.2); - w.write_bd(fap.0); - w.write_bd(fap.1); - w.write_bd(fap.2); - w.write_bd(ll); - } - - #[test] - fn roundtrip_radius_r2000() { - let mut w = BitWriter::new(); - write_common(&mut w, Version::R2000); - write_radial_suffix(&mut w, (0.0, 0.0, 0.0), (5.0, 0.0, 0.0), 2.5); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let common = read_common(&mut c, Version::R2000).unwrap(); - let d = decode_radius(&mut c, &common, Version::R2000).unwrap(); - assert_eq!(d.suffix.center_point.x, 0.0); - assert_eq!(d.suffix.first_arc_point.x, 5.0); - assert_eq!(d.suffix.leader_length, 2.5); - } - - #[test] - fn roundtrip_diameter_r2000() { - let mut w = BitWriter::new(); - write_common(&mut w, Version::R2000); - write_radial_suffix(&mut w, (10.0, 10.0, 0.0), (20.0, 10.0, 0.0), 4.0); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let common = read_common(&mut c, Version::R2000).unwrap(); - let d = decode_diameter(&mut c, &common, Version::R2000).unwrap(); - assert_eq!(d.suffix.center_point.x, 10.0); - assert_eq!(d.suffix.first_arc_point.x, 20.0); - assert_eq!(d.suffix.leader_length, 4.0); - } - - #[test] - fn radius_and_diameter_share_layout() { - // Two identical streams should decode to identical suffixes. - let mut w1 = BitWriter::new(); - write_common(&mut w1, Version::R2000); - write_radial_suffix(&mut w1, (1.0, 2.0, 0.0), (4.0, 2.0, 0.0), 0.5); - let bytes1 = w1.into_bytes(); - - let mut w2 = BitWriter::new(); - write_common(&mut w2, Version::R2000); - write_radial_suffix(&mut w2, (1.0, 2.0, 0.0), (4.0, 2.0, 0.0), 0.5); - let bytes2 = w2.into_bytes(); - - let mut c1 = BitCursor::new(&bytes1); - let common1 = read_common(&mut c1, Version::R2000).unwrap(); - let r = decode_radius(&mut c1, &common1, Version::R2000).unwrap(); - - let mut c2 = BitCursor::new(&bytes2); - let common2 = read_common(&mut c2, Version::R2000).unwrap(); - let d = decode_diameter(&mut c2, &common2, Version::R2000).unwrap(); - - assert_eq!(r.suffix, d.suffix); - } -} diff --git a/src/entities/ellipse.rs b/src/entities/ellipse.rs index 404b196..f7d155a 100644 --- a/src/entities/ellipse.rs +++ b/src/entities/ellipse.rs @@ -47,35 +47,3 @@ pub fn decode(c: &mut BitCursor<'_>) -> Result { end_param, }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - - #[test] - fn roundtrip_ellipse() { - let mut w = BitWriter::new(); - // center - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - // major axis (10, 0, 0) - w.write_bd(10.0); - w.write_bd(0.0); - w.write_bd(0.0); - // extrusion (0, 0, 1) - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - // axis_ratio - w.write_bd(0.5); - w.write_bd(0.0); - w.write_bd(std::f64::consts::TAU); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let e = decode(&mut c).unwrap(); - assert_eq!(e.axis_ratio, 0.5); - assert_eq!(e.major_axis.x, 10.0); - } -} diff --git a/src/entities/endblk.rs b/src/entities/endblk.rs index 6ddbe80..8e7baa3 100644 --- a/src/entities/endblk.rs +++ b/src/entities/endblk.rs @@ -15,16 +15,3 @@ pub struct EndBlk; pub fn decode(_c: &mut BitCursor<'_>) -> Result { Ok(EndBlk) } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn endblk_is_empty() { - // Any cursor position is valid since there's nothing to read. - let buf: [u8; 0] = []; - let mut c = BitCursor::new(&buf); - let _ = decode(&mut c).unwrap(); - } -} diff --git a/src/entities/extruded_surface.rs b/src/entities/extruded_surface.rs index 761ccff..70bf886 100644 --- a/src/entities/extruded_surface.rs +++ b/src/entities/extruded_surface.rs @@ -55,67 +55,3 @@ pub fn decode(c: &mut BitCursor<'_>) -> Result { draft_angle, }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - use crate::entities::modeler::tests::write_sat_blob; - - #[test] - fn roundtrip_extruded_surface_with_blob() { - let mut w = BitWriter::new(); - let blob = SatBlob { - empty: false, - version: 1, - bytes: b"DUMMY SAT BYTES".to_vec(), - }; - write_sat_blob(&mut w, &blob); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(10.0); // sweep +Z, 10 units - w.write_bd(0.1_f64); // draft - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let s = decode(&mut c).unwrap(); - assert_eq!(s.sat, blob); - assert_eq!( - s.sweep_vector, - Vec3D { - x: 0.0, - y: 0.0, - z: 10.0 - } - ); - assert!((s.draft_angle - 0.1).abs() < 1e-12); - } - - #[test] - fn roundtrip_extruded_surface_empty_blob() { - let mut w = BitWriter::new(); - write_sat_blob( - &mut w, - &SatBlob { - empty: true, - version: 0, - bytes: Vec::new(), - }, - ); - w.write_bd(1.0); - w.write_bd(2.0); - w.write_bd(3.0); - w.write_bd(0.0); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let s = decode(&mut c).unwrap(); - assert!(s.sat.empty); - assert_eq!( - s.sweep_vector, - Vec3D { - x: 1.0, - y: 2.0, - z: 3.0 - } - ); - } -} diff --git a/src/entities/hatch.rs b/src/entities/hatch.rs index fffe5be..46e6285 100644 --- a/src/entities/hatch.rs +++ b/src/entities/hatch.rs @@ -617,352 +617,3 @@ fn decode_pattern_line(c: &mut BitCursor<'_>) -> Result { /// Back-compat alias. Prefer [`decode`]. pub use decode as decode_header; - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - use crate::string_stream::tests::{bits_of, build_payload}; - - /// Write the R2004+ gradient block. §20.4.75 writes it on every - /// R2004+ HATCH, gradient or not — the flag only says whether the - /// hatch *uses* it. - fn write_gradient_block(w: &mut BitWriter, is_gradient: u32, colors: &[(f64, u32)]) { - w.write_bl(is_gradient as i32); - w.write_bl(0); // reserved - w.write_bd(45.0); // angle - w.write_bd(0.5); // shift - w.write_bl(1); // single colour - w.write_bd(0.75); // tint - w.write_bl(colors.len() as i32); - for (unknown, rgb) in colors { - w.write_bd(*unknown); - w.write_bs(0); - w.write_bl(*rgb as i32); - w.write_rc(0); - } - // The gradient name `TV` slot; inline on pre-R2007. - w.write_bs_u(0); - } - - fn write_tv_8bit(w: &mut BitWriter, bytes: &[u8]) { - w.write_bs_u(bytes.len() as u16); - for b in bytes { - w.write_rc(*b); - } - } - - /// Header of a non-gradient R2004 HATCH up to `num_paths`. - fn write_hatch_header(w: &mut BitWriter, pattern_name: &[u8], solid_fill: bool) { - write_gradient_block(w, 0, &[]); - w.write_bd(0.0); // elevation - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); // extrusion (0,0,1) - write_tv_8bit(w, pattern_name); - w.write_b(solid_fill); - w.write_b(false); // associative - } - - /// Tail of a solid-fill hatch: style, pattern type, no pixel size - /// (no path has the derived bit), no seed points. - fn write_solid_tail(w: &mut BitWriter) { - w.write_bs_u(0); // style - w.write_bs_u(1); // pattern type - w.write_bl(0); // num seed points - } - - #[test] - fn roundtrip_solid_fill_no_paths() { - let mut w = BitWriter::new(); - write_hatch_header(&mut w, b"SOLID", true); - w.write_bl(0); // 0 paths - write_solid_tail(&mut w); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let h = decode(&mut c, Version::R2004).unwrap(); - assert_eq!(h.pattern_name, "SOLID"); - assert!(h.solid_fill); - assert!(!h.associative); - assert!(h.paths.is_empty()); - assert!(h.gradient.is_none()); - assert!(h.pattern_lines.is_empty()); - assert!(h.seed_points.is_empty()); - assert_eq!(h.pattern_type, 1); - } - - #[test] - fn roundtrip_polyline_path() { - let mut w = BitWriter::new(); - write_hatch_header(&mut w, b"ANSI31", true); - w.write_bl(1); // 1 path - w.write_bl_u(0x02 | 0x10); // polyline + outermost - w.write_b(false); // has_bulge - w.write_b(true); // is_closed - w.write_bl(3); // num vertices - for (x, y) in [(0.0f64, 0.0), (10.0, 0.0), (10.0, 10.0)] { - w.write_rd(x); - w.write_rd(y); - } - w.write_bl(0); // num boundary handles - write_solid_tail(&mut w); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let h = decode(&mut c, Version::R2004).unwrap(); - assert_eq!(h.paths.len(), 1); - assert_eq!(h.paths[0].flags, 0x12); - assert_eq!(h.paths[0].num_boundary_handles, 0); - match &h.paths[0].segments { - HatchPathSegments::Polyline { - has_bulge, - is_closed, - vertices, - } => { - assert!(!has_bulge); - assert!(is_closed); - assert_eq!(vertices.len(), 3); - assert_eq!(vertices[0].0, Point2D { x: 0.0, y: 0.0 }); - assert_eq!(vertices[2].0, Point2D { x: 10.0, y: 10.0 }); - assert!(vertices[0].1.is_none()); - } - other => panic!("expected Polyline, got {other:?}"), - } - } - - #[test] - fn roundtrip_line_edge_path() { - let mut w = BitWriter::new(); - write_hatch_header(&mut w, b"ANSI31", true); - w.write_bl(1); // 1 path - w.write_bl_u(0x01); // external (edge list) - w.write_bl(4); // 4 edges - let square = [ - ((0.0f64, 0.0), (10.0, 0.0)), - ((10.0, 0.0), (10.0, 10.0)), - ((10.0, 10.0), (0.0, 10.0)), - ((0.0, 10.0), (0.0, 0.0)), - ]; - for ((sx, sy), (ex, ey)) in square { - w.write_rc(1); // line - w.write_rd(sx); - w.write_rd(sy); - w.write_rd(ex); - w.write_rd(ey); - } - w.write_bl(2); // num boundary handles — the handles are in the handle stream - write_solid_tail(&mut w); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let h = decode(&mut c, Version::R2004).unwrap(); - assert_eq!(h.paths.len(), 1); - assert_eq!(h.paths[0].num_boundary_handles, 2); - match &h.paths[0].segments { - HatchPathSegments::Edges(edges) => { - assert_eq!(edges.len(), 4); - match &edges[0] { - HatchEdge::Line { start, end } => { - assert_eq!(*start, Point2D { x: 0.0, y: 0.0 }); - assert_eq!(*end, Point2D { x: 10.0, y: 0.0 }); - } - other => panic!("expected Line, got {other:?}"), - } - match &edges[3] { - HatchEdge::Line { start, end } => { - assert_eq!(*start, Point2D { x: 0.0, y: 10.0 }); - assert_eq!(*end, Point2D { x: 0.0, y: 0.0 }); - } - other => panic!("expected Line, got {other:?}"), - } - } - other => panic!("expected Edges, got {other:?}"), - } - } - - #[test] - fn decode_errors_on_oversized_paths() { - let mut w = BitWriter::new(); - write_hatch_header(&mut w, b"SOLID", true); - w.write_bl(20_000); // over cap - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let err = decode(&mut c, Version::R2004).unwrap_err(); - assert!( - matches!(&err, Error::SectionMap(msg) if msg.contains("num_paths")), - "err={err:?}" - ); - } - - #[test] - fn decode_errors_on_oversized_segs() { - let mut w = BitWriter::new(); - write_hatch_header(&mut w, b"SOLID", true); - w.write_bl(1); // 1 path - w.write_bl_u(0x01); // edge path - w.write_bl(200_000); // over cap - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let err = decode(&mut c, Version::R2004).unwrap_err(); - assert!( - matches!(&err, Error::SectionMap(msg) if msg.contains("num_path_segs")), - "err={err:?}" - ); - } - - #[test] - fn roundtrip_gradient_fill() { - let mut w = BitWriter::new(); - write_gradient_block(&mut w, 1, &[(0.0, 0x00FF_0000), (1.0, 0x0000_00FF)]); - w.write_bd(0.0); // elevation - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); // extrusion - write_tv_8bit(&mut w, b"SOLID"); - w.write_b(true); // solid fill - w.write_b(false); // associative - w.write_bl(0); // 0 paths - write_solid_tail(&mut w); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let h = decode(&mut c, Version::R2004).unwrap(); - let g = h.gradient.expect("gradient should be present"); - assert_eq!(g.angle, 45.0); - assert_eq!(g.shift, 0.5); - assert_eq!(g.is_single_color, 1); - assert_eq!(g.tint, 0.75); - assert_eq!(g.colors.len(), 2); - assert_eq!(g.colors[0].rgb, 0x00FF_0000); - assert_eq!(g.colors[1].rgb, 0x0000_00FF); - } - - /// A non-solid hatch writes the pattern-definition block; a derived - /// path (`pathflag & 4`) adds the `BD` pixel size. - #[test] - fn roundtrip_pattern_lines_and_seed_points() { - let mut w = BitWriter::new(); - write_hatch_header(&mut w, b"ANSI31", false); - w.write_bl(1); // one path - w.write_bl_u(0x04); // derived path → pixel size is written - w.write_bl(0); // no edges - w.write_bl(0); // no boundary handles - w.write_bs_u(1); // style - w.write_bs_u(1); // pattern type - w.write_bd(45.0); // pattern angle - w.write_bd(2.0); // pattern scale - w.write_b(false); // pattern double - w.write_bs_u(2); // num pattern lines - w.write_bd(0.0); // line 0 angle - w.write_bd(0.0); - w.write_bd(0.0); // origin - w.write_bd(1.0); - w.write_bd(0.0); // offset - w.write_bs_u(2); - w.write_bd(1.0); - w.write_bd(-0.5); - w.write_bd(90.0); // line 1 angle - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bs_u(0); - w.write_bd(4.0); // pixel size - w.write_bl(1); // one seed point - w.write_rd(5.0); - w.write_rd(5.0); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let h = decode(&mut c, Version::R2004).unwrap(); - assert_eq!(h.pattern_style, 1); - assert_eq!(h.pattern_angle, 45.0); - assert_eq!(h.pattern_scale, 2.0); - assert_eq!(h.pattern_lines.len(), 2); - assert_eq!(h.pattern_lines[0].dashes, vec![1.0, -0.5]); - assert_eq!(h.pattern_lines[0].angle, 0.0); - assert_eq!(h.pattern_lines[1].angle, 90.0); - assert!(h.pattern_lines[1].dashes.is_empty()); - assert_eq!(h.pixel_size, 4.0); - assert_eq!(h.seed_points.len(), 1); - assert_eq!(h.seed_points[0], (5.0, 5.0)); - } - - /// The R2018 shape: two strings in the string stream — the gradient - /// name and then the pattern name — even on a hatch whose gradient - /// flag is clear, and the data fields ending exactly on the - /// string-stream start bit. - #[test] - fn r2018_split_stream_hatch_reads_both_names() { - let mut w = BitWriter::new(); - w.write_bs_u(0); // no XDATA - w.write_b(false); // no graphics preview - w.write_bb(0b10); - w.write_bl(0); - w.write_b(true); - w.write_b(false); - w.write_bs_u(0x0100); - w.write_bd(1.0); - w.write_bb(0b00); - w.write_bb(0b00); - w.write_bb(0b00); - w.write_rc(0); - w.write_b(false); - w.write_b(false); - w.write_b(false); - w.write_bs(0); - w.write_rc(0x1D); - // Gradient block with the flag clear — the `TV` slot consumes - // no data bits on R2007+. - w.write_bl(0); - w.write_bl(0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bl(0); - w.write_bd(0.0); - w.write_bl(0); - w.write_bd(0.0); // elevation - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); // extrusion, as the sample writes it - w.write_b(true); // solid fill - w.write_b(false); // associative - w.write_bl(0); // no paths - w.write_bs_u(0); // style - w.write_bs_u(1); // pattern type - w.write_bl(0); // no seed points - let body = bits_of(&w); - let payload = build_payload(&body, &["LINEAR", "ANSI31"]); - let h = decode_modern_split_stream(&payload, 8, Version::R2018).unwrap(); - assert!(h.gradient.is_none()); - assert_eq!(h.pattern_name, "ANSI31"); - assert!(h.solid_fill); - assert_eq!(h.pattern_type, 1); - } - - /// The clear-flag early return this decoder used to take consumed - /// one `TV` too few, so the pattern name came back as the gradient - /// name and the record misaligned. Guard against the regression. - #[test] - fn r2018_clear_gradient_flag_still_consumes_the_gradient_name() { - let mut w = BitWriter::new(); - w.write_bl(0); // gradient flag clear - w.write_bl(0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bl(0); - w.write_bd(0.0); - w.write_bl(0); - write_tv_8bit(&mut w, b"LINEAR"); // gradient name, inline on R2004 - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - write_tv_8bit(&mut w, b"ANSI31"); - w.write_b(true); - w.write_b(false); - w.write_bl(0); - write_solid_tail(&mut w); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let h = decode(&mut c, Version::R2004).unwrap(); - assert!(h.gradient.is_none()); - assert_eq!(h.pattern_name, "ANSI31"); - } -} diff --git a/src/entities/helix.rs b/src/entities/helix.rs index 51f0b65..1d2431c 100644 --- a/src/entities/helix.rs +++ b/src/entities/helix.rs @@ -103,94 +103,3 @@ pub fn decode(c: &mut BitCursor<'_>) -> Result { constrain_type, }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - - #[test] - fn roundtrip_right_handed_helix() { - let mut w = BitWriter::new(); - // axis base at origin - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - // start at (radius, 0, 0) - w.write_bd(5.0); - w.write_bd(0.0); - w.write_bd(0.0); - // axis +Z - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - // radius 5 - w.write_bd(5.0); - // 3 turns - w.write_bd(3.0); - // turn height 2 (total height 6) - w.write_bd(2.0); - // right-handed - w.write_b(true); - // constrain by turn-height - w.write_bs(0); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let h = decode(&mut c).unwrap(); - assert_eq!(h.axis_base_point, Point3D::default()); - assert_eq!( - h.start_point, - Point3D { - x: 5.0, - y: 0.0, - z: 0.0 - } - ); - assert!((h.radius - 5.0).abs() < 1e-12); - assert!((h.turns - 3.0).abs() < 1e-12); - assert!(h.right_handed); - assert_eq!(h.constrain_type, ConstrainType::TurnHeight); - } - - #[test] - fn roundtrip_left_handed_fractional_turns() { - let mut w = BitWriter::new(); - for _ in 0..3 { - // three points, no origin shift for simplicity - w.write_bd(1.0); - w.write_bd(2.0); - w.write_bd(3.0); - } - w.write_bd(2.5); // radius - w.write_bd(1.25); // turns — fractional - w.write_bd(1.0); // turn height - w.write_b(false); // left-handed - w.write_bs(2); // constrain by height - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let h = decode(&mut c).unwrap(); - assert!(!h.right_handed); - assert!((h.turns - 1.25).abs() < 1e-12); - assert_eq!(h.constrain_type, ConstrainType::Height); - } - - #[test] - fn rejects_bad_constrain_enum() { - assert!(ConstrainType::from_bs(3).is_err()); - assert!(matches!( - ConstrainType::from_bs(-1).unwrap_err(), - Error::SectionMap(_) - )); - } - - #[test] - fn constrain_type_roundtrip() { - for ct in [ - ConstrainType::TurnHeight, - ConstrainType::Turns, - ConstrainType::Height, - ] { - assert_eq!(ConstrainType::from_bs(ct.to_bs()).unwrap(), ct); - } - } -} diff --git a/src/entities/image.rs b/src/entities/image.rs index 0eb5a5e..5be66a4 100644 --- a/src/entities/image.rs +++ b/src/entities/image.rs @@ -156,128 +156,3 @@ pub fn decode(c: &mut BitCursor<'_>, version: Version) -> Result { clip_boundary, }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - - #[test] - fn roundtrip_image_with_rect_clip() { - let mut w = BitWriter::new(); - w.write_bl(0); - w.write_bd(10.0); - w.write_bd(20.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(0.0); - w.write_rd(1920.0); - w.write_rd(1080.0); - w.write_bs(0x07); // show + show-not-aligned + clip - w.write_b(true); // clipping - w.write_rc(50); - w.write_rc(50); - w.write_rc(0); - w.write_bs(1); // rectangle — no vertex count follows - w.write_rd(0.0); - w.write_rd(0.0); - w.write_rd(1920.0); - w.write_rd(1080.0); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let i = decode(&mut c, Version::R2000).unwrap(); - assert_eq!( - i.image_size, - Point2D { - x: 1920.0, - y: 1080.0 - } - ); - assert!(matches!(i.clip_boundary, ClipBoundary::Rectangle { .. })); - } - - /// The measured `sample_AC1032.dwg` IMAGE body (handle `0x662`): - /// a 994 × 965 raster whose rectangular clip boundary follows the - /// boundary type with no vertex count in between. - #[test] - fn decodes_the_measured_rectangle_image_body() { - let mut w = BitWriter::new(); - w.write_bl(0); - w.write_bd(0.0); // insertion point (0, 15, 0) - w.write_bd(15.0); - w.write_bd(0.0); - w.write_bd(0.010416666666666664); // u vector = 1/96 in X - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(6.37836874555913e-19); // v vector = 1/96 in Y - w.write_bd(0.010416666666666666); - w.write_bd(0.0); - w.write_rd(994.0); // image size in pixels - w.write_rd(965.0); - w.write_bs(7); // display flags - w.write_b(false); // clipping off - w.write_rc(50); - w.write_rc(50); - w.write_rc(0); - w.write_b(false); // clip mode (R2010+) - w.write_bs(1); // rectangle - w.write_rd(-0.5); // corners, straight after the type - w.write_rd(-0.5); - w.write_rd(993.5); - w.write_rd(964.5); - let end = w.position_bits(); - - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let i = decode(&mut c, Version::R2018).unwrap(); - assert_eq!(i.image_size, Point2D { x: 994.0, y: 965.0 }); - assert_eq!(i.brightness, 50); - assert_eq!( - i.clip_boundary, - ClipBoundary::Rectangle { - lower_left: Point2D { x: -0.5, y: -0.5 }, - upper_right: Point2D { x: 993.5, y: 964.5 }, - } - ); - assert_eq!(c.position_bits(), end); - } - - #[test] - fn roundtrip_image_with_poly_clip() { - let mut w = BitWriter::new(); - w.write_bl(0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(0.0); - w.write_rd(100.0); - w.write_rd(100.0); - w.write_bs(0x01); - w.write_b(true); - w.write_rc(50); - w.write_rc(50); - w.write_rc(0); - w.write_bs(2); // polygon - w.write_bl(3); - for (x, y) in [(0.0f64, 0.0), (100.0, 0.0), (50.0, 100.0)] { - w.write_rd(x); - w.write_rd(y); - } - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let i = decode(&mut c, Version::R2000).unwrap(); - match i.clip_boundary { - ClipBoundary::Polygon(pts) => assert_eq!(pts.len(), 3), - _ => panic!("expected polygon"), - } - } -} diff --git a/src/entities/imagedef.rs b/src/entities/imagedef.rs index 3b34162..bf82666 100644 --- a/src/entities/imagedef.rs +++ b/src/entities/imagedef.rs @@ -152,83 +152,3 @@ fn read_tv(c: &mut BitCursor<'_>, version: Version) -> Result { Ok(crate::code_page::decode_active(&bytes)) } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - - fn write_tv_8bit(w: &mut BitWriter, s: &str) { - w.write_bs_u((s.len() + 1) as u16); // +1 for trailing NUL - for b in s.as_bytes() { - w.write_rc(*b); - } - w.write_rc(0); - } - - #[test] - fn roundtrip_minimal_imagedef() { - let mut w = BitWriter::new(); - w.write_bl(0); // class_version - w.write_rd(1920.0); - w.write_rd(1080.0); - write_tv_8bit(&mut w, "C:\\drawings\\bg.png"); - w.write_b(true); // is_loaded - w.write_rc(1); // mm - w.write_rd(0.5); - w.write_rd(0.5); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let i = decode(&mut c, Version::R2000).unwrap(); - assert_eq!(i.class_version, 0); - assert_eq!(i.image_size_pixels, (1920.0, 1080.0)); - assert_eq!(i.file_path, "C:\\drawings\\bg.png"); - assert!(i.is_loaded); - assert_eq!(i.pixel_size_units, 1); - assert_eq!(i.pixel_width_size, 0.5); - assert_eq!(i.pixel_height_size, 0.5); - } - - #[test] - fn roundtrip_empty_path() { - let mut w = BitWriter::new(); - w.write_bl(0); - w.write_rd(0.0); - w.write_rd(0.0); - w.write_bs_u(0); // empty TV - w.write_b(false); - w.write_rc(0); // unitless - w.write_rd(1.0); - w.write_rd(1.0); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let i = decode(&mut c, Version::R2000).unwrap(); - assert_eq!(i.file_path, ""); - assert!(!i.is_loaded); - assert_eq!(i.pixel_size_units, 0); - } - - #[test] - fn rejects_oversized_tv_claim() { - // Claim a path length larger than the cap — must reject before - // any RC reads (defensive-allocation). - let mut w = BitWriter::new(); - w.write_bl(0); - w.write_rd(0.0); - w.write_rd(0.0); - // Claim IMAGEDEF_MAX_PATH_UNITS + 1 — beyond the cap. - // Because write_bs_u is u16 we can't encode past 65535; use the - // cap itself (65536) by writing 0 in the BS_U and... actually - // bs_u is limited to u16 so max is 65535. The cap catches - // 65536+; use remaining_bits branch for this test instead. - // - // Drop a large claim (within u16) but with no payload behind - // it — remaining_bits will be smaller than the claim. - w.write_bs_u(60_000); - // no payload - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let err = decode(&mut c, Version::R2000).unwrap_err(); - assert!(matches!(&err, Error::SectionMap(msg) if msg.contains("IMAGEDEF"))); - } -} diff --git a/src/entities/lofted_surface.rs b/src/entities/lofted_surface.rs index bb2caa7..ae30583 100644 --- a/src/entities/lofted_surface.rs +++ b/src/entities/lofted_surface.rs @@ -83,95 +83,3 @@ pub fn decode(c: &mut BitCursor<'_>) -> Result { end_tangent_mag, }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - use crate::entities::modeler::tests::write_sat_blob; - - #[test] - fn roundtrip_loft_three_sections() { - let mut w = BitWriter::new(); - write_sat_blob( - &mut w, - &SatBlob { - empty: false, - version: 1, - bytes: b"LOFT".to_vec(), - }, - ); - w.write_bl(3); - w.write_handle(2, 0x10); - w.write_handle(2, 0x11); - w.write_handle(2, 0x12); - w.write_bd(1.0); - w.write_bd(1.5); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let s = decode(&mut c).unwrap(); - assert_eq!(s.cross_section_handles.len(), 3); - assert_eq!(s.cross_section_handles[0].value, 0x10); - assert_eq!(s.cross_section_handles[2].value, 0x12); - assert!((s.start_tangent_mag - 1.0).abs() < 1e-12); - assert!((s.end_tangent_mag - 1.5).abs() < 1e-12); - } - - #[test] - fn roundtrip_loft_zero_sections() { - // Degenerate but legal: a loft stub with no cross-sections. - let mut w = BitWriter::new(); - write_sat_blob( - &mut w, - &SatBlob { - empty: true, - version: 0, - bytes: Vec::new(), - }, - ); - w.write_bl(0); - w.write_bd(0.0); - w.write_bd(0.0); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let s = decode(&mut c).unwrap(); - assert!(s.cross_section_handles.is_empty()); - } - - #[test] - fn rejects_negative_count() { - let mut w = BitWriter::new(); - write_sat_blob( - &mut w, - &SatBlob { - empty: true, - version: 0, - bytes: Vec::new(), - }, - ); - w.write_bl(-1); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let err = decode(&mut c).unwrap_err(); - assert!(matches!(err, Error::SectionMap(_))); - } - - #[test] - fn rejects_implausible_count() { - // Claim more cross-sections than the cap allows. - let mut w = BitWriter::new(); - write_sat_blob( - &mut w, - &SatBlob { - empty: true, - version: 0, - bytes: Vec::new(), - }, - ); - w.write_bl(i32::MAX / 2); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let err = decode(&mut c).unwrap_err(); - assert!(matches!(err, Error::SectionMap(_))); - } -} diff --git a/src/entities/mesh.rs b/src/entities/mesh.rs index a4ac590..60fd6ef 100644 --- a/src/entities/mesh.rs +++ b/src/entities/mesh.rs @@ -246,190 +246,3 @@ pub fn decode(c: &mut BitCursor<'_>, _version: Version) -> Result { creases, }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - - /// Scalar fields shared by every MESH test fixture. Kept separate - /// from the list-valued fields so `write_mesh` stays under the - /// 7-arg clippy ceiling without deeply nesting the test payload - /// definitions. - struct MeshHeader { - version: u16, - blend_crease: bool, - subdivision_level: u16, - } - - /// Helper — write a minimal MESH payload for the R2010+ shape. - fn write_mesh( - w: &mut BitWriter, - hdr: &MeshHeader, - vertices: &[Point3D], - faces: &[&[u32]], - edges: &[(u32, u32)], - creases: &[f64], - ) { - w.write_bs_u(hdr.version); - w.write_b(hdr.blend_crease); - w.write_bs_u(hdr.subdivision_level); - w.write_bs_u(vertices.len() as u16); - for v in vertices { - w.write_bd(v.x); - w.write_bd(v.y); - w.write_bd(v.z); - } - // The face list is sized in BL entries: one count per face plus - // that face's indices. - let face_list_size: usize = faces.iter().map(|f| 1 + f.len()).sum(); - w.write_bl(face_list_size as i32); - for f in faces { - w.write_bl(f.len() as i32); - for &i in *f { - w.write_bl(i as i32); - } - } - w.write_bl(edges.len() as i32); - for &(a, b) in edges { - w.write_bl_u(a); - w.write_bl_u(b); - } - w.write_bl(creases.len() as i32); - for &c in creases { - w.write_bd(c); - } - w.write_bl(0); - } - - #[test] - fn roundtrip_minimal_cube_cage() { - let mut w = BitWriter::new(); - // A 2-face cage sharing one edge — enough to cover every count - // path without drowning the test in coordinates. - let verts = [ - Point3D { - x: 0.0, - y: 0.0, - z: 0.0, - }, - Point3D { - x: 1.0, - y: 0.0, - z: 0.0, - }, - Point3D { - x: 1.0, - y: 1.0, - z: 0.0, - }, - Point3D { - x: 0.0, - y: 1.0, - z: 0.0, - }, - ]; - let face_a: &[u32] = &[0, 1, 2]; - let face_b: &[u32] = &[0, 2, 3]; - let edges = [(0u32, 1u32), (1, 2), (2, 0), (2, 3), (3, 0)]; - let creases = [0.0f64, 0.0, 1.0, 0.0, 0.0]; - write_mesh( - &mut w, - &MeshHeader { - version: 0, - blend_crease: false, - subdivision_level: 2, - }, - &verts, - &[face_a, face_b], - &edges, - &creases, - ); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let m = decode(&mut c, Version::R2010).unwrap(); - assert_eq!(m.version, 0); - assert!(!m.blend_crease); - assert_eq!(m.subdivision_level, 2); - assert_eq!(m.vertices.len(), 4); - assert_eq!( - m.vertices[1], - Point3D { - x: 1.0, - y: 0.0, - z: 0.0 - } - ); - assert_eq!(m.faces.len(), 2); - assert_eq!(m.faces[0], vec![0, 1, 2]); - assert_eq!(m.faces[1], vec![0, 2, 3]); - assert_eq!(m.edges.len(), 5); - assert_eq!(m.edges[2], (2, 0)); - assert_eq!(m.creases, vec![0.0, 0.0, 1.0, 0.0, 0.0]); - } - - #[test] - fn roundtrip_blend_crease_true_higher_level() { - let mut w = BitWriter::new(); - let verts = [ - Point3D { - x: 0.0, - y: 0.0, - z: 0.0, - }, - Point3D { - x: 2.0, - y: 0.0, - z: 0.0, - }, - Point3D { - x: 1.0, - y: 2.0, - z: 0.0, - }, - ]; - let face: &[u32] = &[0, 1, 2]; - let edges = [(0u32, 1u32), (1, 2), (2, 0)]; - let creases = [0.5f64, 0.0, 0.25]; - write_mesh( - &mut w, - &MeshHeader { - version: 1, - blend_crease: true, - subdivision_level: 4, - }, - &verts, - &[face], - &edges, - &creases, - ); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let m = decode(&mut c, Version::R2013).unwrap(); - assert_eq!(m.version, 1); - assert!(m.blend_crease); - assert_eq!(m.subdivision_level, 4); - assert_eq!(m.faces[0].len(), 3); - assert_eq!(m.creases[0], 0.5); - assert_eq!(m.creases[2], 0.25); - } - - #[test] - fn rejects_oversized_face_list_size() { - let mut w = BitWriter::new(); - // Version + blend + subdivision_level + vertex_count(0) - w.write_bs_u(0); - w.write_b(false); - w.write_bs_u(0); - w.write_bs_u(0); - // face_list_size far beyond CAP_FACE_LIST - w.write_bl(20_000_000); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let err = decode(&mut c, Version::R2010).unwrap_err(); - assert!( - matches!(&err, Error::SectionMap(msg) if msg.contains("face_list_size")), - "err={err:?}" - ); - } -} diff --git a/src/entities/mleader.rs b/src/entities/mleader.rs index b4e14da..3b44ca0 100644 --- a/src/entities/mleader.rs +++ b/src/entities/mleader.rs @@ -921,247 +921,3 @@ pub fn decode(c: &mut BitCursor<'_>, version: Version) -> Result { }; read_body(c, &mut s, version) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - use crate::string_stream::tests::build_payload; - - /// Append the bits of one BitWriter onto a bit vector. - fn bits(w: BitWriter) -> Vec { - crate::string_stream::tests::bits_of(&w) - } - - /// Write a `CMC` with no name suffixes. - fn write_cmc(w: &mut BitWriter, rgb: u32) { - w.write_bs(0); - w.write_bl(rgb as i32); - w.write_rc(0); - } - - /// A minimal but complete R2018 MULTILEADER body: one leader root - /// with one two-point leader line, MTEXT content, no columns. - fn synth_body() -> Vec { - let mut w = BitWriter::new(); - w.write_bs(2); // class version (270) - // -- context -- - w.write_bl(1); // one leader root - w.write_b(true); // content valid - w.write_b(true); // unknown - w.write_bd(10.0); // connection point - w.write_bd(20.0); - w.write_bd(0.0); - w.write_bd(1.0); // direction - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bl(0); // no break pairs - w.write_bl(0); // leader index - w.write_bd(8.0); // landing distance - w.write_bl(1); // one leader line - w.write_bl(2); // two points - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(5.0); - w.write_bd(5.0); - w.write_bd(0.0); - w.write_bl(0); // no break info - w.write_bl(0); // leader line index - w.write_bs(1); // leader type = straight - write_cmc(&mut w, 0xC100_0000); - w.write_bl(-2); // line weight - w.write_bd(0.0); // arrow size - w.write_bl(0); // override flags - w.write_bs(0); // attachment direction (root) - w.write_bd(1.0); // overall scale - w.write_bd(1.0); // content base point - w.write_bd(2.0); - w.write_bd(0.0); - w.write_bd(4.0); // text height - w.write_bd(4.0); // arrow head size - w.write_bd(2.0); // landing gap - w.write_bs(1); // left attachment - w.write_bs(1); // right attachment - w.write_bs(0); // text align type - w.write_bs(0); // attachment type - w.write_b(true); // has text contents - // text branch: the TV slot consumes no data bits - w.write_bd(0.0); // normal - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(3.0); // location - w.write_bd(4.0); - w.write_bd(0.0); - w.write_bd(1.0); // direction - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); // rotation - w.write_bd(0.0); // boundary width - w.write_bd(0.0); // boundary height - w.write_bd(1.0); // line spacing factor - w.write_bs(1); // line spacing style - write_cmc(&mut w, 0xC000_0000); // text colour - w.write_bs(1); // alignment - w.write_bs(5); // flow direction - write_cmc(&mut w, 0xC000_0000); // background colour - w.write_bd(0.0); // background scale - w.write_bl(0); // background transparency - w.write_b(false); // background fill - w.write_b(false); // background mask fill - w.write_bs(0); // column type - w.write_b(false); // text height automatic - w.write_bd(0.0); // column width - w.write_bd(0.0); // column gutter - w.write_b(false); // column flow reversed - w.write_bl(0); // no column sizes - w.write_b(false); // word break - w.write_b(false); // unknown - w.write_bd(0.0); // base point - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); // base direction - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); // base vertical - w.write_bd(1.0); - w.write_bd(0.0); - w.write_b(false); // normal reversed - w.write_bs(9); // top attachment - w.write_bs(9); // bottom attachment - // -- style block -- - w.write_bl(279552); // override flags - w.write_bs(1); // leader type - write_cmc(&mut w, 0xC100_0000); // leader colour - w.write_bl(-2); // line weight - w.write_b(true); // landing enabled - w.write_b(true); // dogleg enabled - w.write_bd(8.0); // landing distance - w.write_bd(4.0); // arrow head size - w.write_bs(2); // content type = MTEXT - w.write_bs(1); // left attachment - w.write_bs(1); // right attachment - w.write_bs(1); // text angle type - w.write_bs(0); // unknown 175 - write_cmc(&mut w, 0xC100_0000); // text colour - w.write_b(false); // text frame enabled - write_cmc(&mut w, 0xC100_0000); // block colour - w.write_bd(1.0); // block scale - w.write_bd(1.0); - w.write_bd(1.0); - w.write_bd(0.0); // block rotation - w.write_bs(0); // block attachment type - w.write_b(false); // is annotative - w.write_mc(274).unwrap(); // the undocumented MC - w.write_b(true); // the undocumented flag - w.write_bs(0); // attachment direction - w.write_bs(9); // top attachment - w.write_bs(9); // bottom attachment - w.write_b(false); // leader extended to text (R2013+) - bits(w) - } - - /// Round-trip a BitWriter-built R2018 MULTILEADER through the - /// split-stream decoder, including the exact-boundary check. - #[test] - fn roundtrip_minimal_r2018_multileader() { - // The record body is the common entity preamble followed by the - // MULTILEADER field list; `build_payload` frames it with the - // string stream and its trailer. - let mut pre = BitWriter::new(); - pre.write_bs_u(0); // no XDATA - pre.write_b(false); // no graphics preview - pre.write_bb(0b10); // entmode - pre.write_bl(0); // num_reactors - pre.write_b(true); // no xdictionary - pre.write_b(false); // no AcDs binary data - pre.write_bs_u(0x0100); // colour - pre.write_bd(1.0); // linetype scale - pre.write_bb(0b00); // ltype flags - pre.write_bb(0b00); // plotstyle - pre.write_bb(0b00); // material - pre.write_rc(0); // shadow - pre.write_b(false); - pre.write_b(false); - pre.write_b(false); - pre.write_bs(0); // invisibility - pre.write_rc(0x1D); // lineweight - let mut body = bits(pre); - body.extend(synth_body()); - - let payload = build_payload(&body, &["MULTILEADER TEST"]); - let m = decode_modern_split_stream(&payload, 8, Version::R2018).expect("decodes"); - assert_eq!(m.class_version, 2); - assert_eq!(m.context.leader_roots.len(), 1); - let root = &m.context.leader_roots[0]; - assert_eq!(root.landing_distance, 8.0); - assert_eq!(root.lines.len(), 1); - assert_eq!(root.lines[0].points.len(), 2); - assert_eq!( - root.lines[0].points[1], - Point3D { - x: 5.0, - y: 5.0, - z: 0.0 - } - ); - assert_eq!(root.lines[0].line_weight, -2); - let text = m.context.text.as_ref().expect("text content"); - assert_eq!(text.label, "MULTILEADER TEST"); - assert_eq!(text.flow_direction, 5); - assert!(m.context.block.is_none()); - assert_eq!(m.context.top_attachment, 9); - assert_eq!(m.override_flags, 279552); - assert_eq!(m.content_type, 2); - assert_eq!( - m.block_scale, - Point3D { - x: 1.0, - y: 1.0, - z: 1.0 - } - ); - assert!(!m.is_annotative); - assert_eq!(m.undocumented_mc, 274); - assert!(m.undocumented_flag); - assert_eq!(m.bottom_attachment, 9); - assert!(!m.leader_extended_to_text); - } - - /// A wrong field list must error on the boundary, not return a - /// plausible struct: dropping the R2013+ `B 295` leaves the data - /// fields one bit short of the string stream. - #[test] - fn misaligned_field_list_errors() { - let mut pre = BitWriter::new(); - pre.write_bs_u(0); - pre.write_b(false); - pre.write_bb(0b10); - pre.write_bl(0); - pre.write_b(true); - pre.write_b(false); - pre.write_bs_u(0x0100); - pre.write_bd(1.0); - pre.write_bb(0b00); - pre.write_bb(0b00); - pre.write_bb(0b00); - pre.write_rc(0); - pre.write_b(false); - pre.write_b(false); - pre.write_b(false); - pre.write_bs(0); - pre.write_rc(0x1D); - let mut body = bits(pre); - let mut synth = synth_body(); - synth.pop(); // drop the R2013+ trailing bit - body.extend(synth); - - let payload = build_payload(&body, &["MULTILEADER TEST"]); - let err = decode_modern_split_stream(&payload, 8, Version::R2018) - .expect_err("a short field list must be rejected"); - assert!( - matches!(&err, Error::SectionMap(m) if m.contains("MULTILEADER data fields ended")), - "err={err:?}" - ); - } -} diff --git a/src/entities/mline.rs b/src/entities/mline.rs index 3d5e19a..d527975 100644 --- a/src/entities/mline.rs +++ b/src/entities/mline.rs @@ -242,134 +242,3 @@ pub fn decode(c: &mut BitCursor<'_>) -> Result { vertices, }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - - #[test] - fn roundtrip_two_line_three_vertex_mline() { - let mut w = BitWriter::new(); - w.write_bd(1.0); // scale factor - w.write_rc(1); // zero justification - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); // scale_point - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); // extrusion - w.write_bs(0); // open - w.write_rc(2); // 2 parallel lines - w.write_bs(3); // 3 vertices - - // Vertex 0 - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); // vertex - w.write_bd(1.0); - w.write_bd(0.0); - w.write_bd(0.0); // seg dir - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(0.0); // miter - // Line 0 sub-record: 0 segment params, 0 area-fill params - w.write_bs(0); - w.write_bs(0); - // Line 1 sub-record: same - w.write_bs(0); - w.write_bs(0); - - // Vertex 1 - w.write_bd(10.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(0.0); - w.write_bs(0); - w.write_bs(0); - w.write_bs(0); - w.write_bs(0); - - // Vertex 2 — with one segment param on line 1 just to exercise - // the inner loop. - w.write_bd(20.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(0.0); - w.write_bs(0); - w.write_bs(0); // line 0 - w.write_bs(1); // line 1: 1 segment param - w.write_bd(0.75); - w.write_bs(0); - - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let m = decode(&mut c).unwrap(); - assert_eq!(m.scale_factor, 1.0); - assert_eq!(m.justification, Justification::Zero); - assert_eq!(m.num_lines, 2); - assert_eq!(m.vertices.len(), 3); - assert_eq!( - m.vertices[1].vertex_point, - Point3D { - x: 10.0, - y: 0.0, - z: 0.0 - } - ); - // Last vertex, line 1, should have exactly one segment param = 0.75 - let last = &m.vertices[2].sub_records[1]; - assert_eq!(last.segment_parameters, vec![0.75]); - assert!(last.area_fill_parameters.is_empty()); - } - - #[test] - fn rejects_implausible_vertex_count() { - let mut w = BitWriter::new(); - w.write_bd(1.0); - w.write_rc(0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bs(0); - w.write_rc(2); - // Claim 30000 vertices but no further payload. - w.write_bs(30000); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - assert!(decode(&mut c).is_err()); - } - - #[test] - fn unknown_justification_passes_through() { - let mut w = BitWriter::new(); - w.write_bd(1.0); - w.write_rc(99); // unknown justification - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bs(0); - w.write_rc(0); // 0 lines - w.write_bs(0); // 0 vertices - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let m = decode(&mut c).unwrap(); - assert_eq!(m.justification, Justification::Unknown(99)); - } -} diff --git a/src/entities/modeler.rs b/src/entities/modeler.rs index 7f32fc4..315c94d 100644 --- a/src/entities/modeler.rs +++ b/src/entities/modeler.rs @@ -337,222 +337,3 @@ pub fn decode_record( }, }) } - -#[cfg(test)] -pub(crate) mod tests { - use super::*; - use crate::bitwriter::BitWriter; - - /// Encode a SAT blob in the same wire shape the underlying - /// `three_d_solid` decoder expects. Keeps the per-entity tests - /// focused on their own fields without re-stating the chunk loop - /// each time. - pub(crate) fn write_sat_blob(w: &mut BitWriter, blob: &SatBlob) { - if blob.empty { - w.write_b(true); - return; - } - w.write_b(false); - w.write_b(false); // §20.4.41 unknown bit - w.write_bs_u(blob.version); - if !blob.bytes.is_empty() { - w.write_bl(blob.bytes.len() as i32); - for b in &blob.bytes { - w.write_rc(*b); - } - } - w.write_bl(0); // terminating block size - } - - /// Write the §20.4.41 tail of a record that carries no wireframe - /// block and no data-store block. - pub(crate) fn write_minimal_tail(w: &mut BitWriter) { - w.write_b(false); // wireframe data present - w.write_b(true); // ACIS empty (2) - w.write_bl(0); // R2007+ unknown - } - - #[test] - fn roundtrip_empty_blob() { - let mut w = BitWriter::new(); - write_sat_blob( - &mut w, - &SatBlob { - empty: true, - version: 0, - bytes: Vec::new(), - }, - ); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let b = decode_sat_blob(&mut c).unwrap(); - assert!(b.empty); - assert!(b.bytes.is_empty()); - } - - #[test] - fn roundtrip_one_chunk_blob() { - let payload = b"ACIS DUMMY BODY DATA".to_vec(); - let mut w = BitWriter::new(); - write_sat_blob( - &mut w, - &SatBlob { - empty: false, - version: 1, - bytes: payload.clone(), - }, - ); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let b = decode_sat_blob(&mut c).unwrap(); - assert!(!b.empty); - assert_eq!(b.version, 1); - assert_eq!(b.bytes, payload); - } - - /// The data-store shape measured on `sample_AC1032.dwg`: no inline - /// envelope, a wireframe block whose point is real geometry and - /// whose isoline count is AutoCAD's default `4`, then the seven - /// unknown bits, the revision GUID and the trailing `BL`. - #[test] - fn roundtrip_r2018_data_store_record() { - let guid: [u8; 16] = [ - 0x83, 0x31, 0x11, 0xA1, 0xB7, 0xAC, 0x4D, 0xD4, 0x82, 0x4D, 0x78, 0xB3, 0x36, 0x68, - 0xF9, 0xE7, - ]; - let mut w = BitWriter::new(); - w.write_b(true); // wireframe data present - w.write_b(true); // point present - w.write_bd(17.776_725_469_823_76); - w.write_bd(-220.850_122_660_715_93); - w.write_bd(2.5); - w.write_bl(4); // num isolines - w.write_b(true); // isolines present - w.write_bl(0); // num wires - w.write_bl(0); // num silhouettes - w.write_b(true); // ACIS empty (2) - w.write_bl(0); // R2007+ unknown - w.write_b(true); // unknown_a - w.write_bb(0); // unknown_b - w.write_bb(0); // unknown_c - w.write_bb(0b10); // unknown_d - for b in guid { - w.write_rc(b); - } - w.write_bl(0); // unknown_e - let end = w.position_bits(); - - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let rec = decode_record(&mut c, Version::R2018, true).unwrap(); - assert!(rec.in_data_store); - assert!(rec.blob.empty); - assert!(rec.tail.wireframe_present); - assert_eq!(rec.tail.num_isolines, 4); - assert!(rec.tail.isolines_present); - assert_eq!(rec.tail.num_wires, 0); - assert_eq!(rec.tail.num_silhouettes, 0); - assert!(rec.tail.acis_empty_2); - assert_eq!(rec.tail.revision_guid, Some(guid)); - let p = rec.tail.point.unwrap(); - assert_eq!(p.z, 2.5); - assert_eq!(c.position_bits(), end); - } - - /// A record with an inline envelope and no wireframe block closes - /// on the same grammar with the data-store branch off. - #[test] - fn roundtrip_inline_envelope_record() { - let mut w = BitWriter::new(); - write_sat_blob( - &mut w, - &SatBlob { - empty: false, - version: 1, - bytes: b"400 0 1 0".to_vec(), - }, - ); - write_minimal_tail(&mut w); - let end = w.position_bits(); - - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let rec = decode_record(&mut c, Version::R2018, false).unwrap(); - assert!(!rec.blob.empty); - assert_eq!(rec.blob.bytes, b"400 0 1 0".to_vec()); - assert!(!rec.tail.wireframe_present); - assert!(rec.tail.acis_empty_2); - assert_eq!(rec.tail.revision_guid, None); - assert_eq!(c.position_bits(), end); - } - - /// A wire list is stepped, not surfaced — but it must still be - /// stepped exactly, or the record misses its boundary. - #[test] - fn wire_and_silhouette_lists_are_stepped_exactly() { - let mut w = BitWriter::new(); - w.write_b(true); // ACIS empty — no inline envelope - w.write_b(true); // wireframe data present - w.write_b(false); // point present - w.write_bl(4); - w.write_b(true); // isolines present - w.write_bl(1); // one wire - w.write_rc(3); // wire type - w.write_bl(7); // selection marker - w.write_bs(256); // colour - w.write_bl(2); // acis index - w.write_bl(2); // point count - for _ in 0..2 { - w.write_bd(1.0); - w.write_bd(2.0); - w.write_bd(3.0); - } - w.write_b(true); // transform present - for _ in 0..4 { - w.write_bd(1.0); - w.write_bd(0.0); - w.write_bd(0.0); - } - w.write_bd(1.0); // scale - w.write_b(false); - w.write_b(false); - w.write_b(false); - w.write_bl(1); // one silhouette - w.write_bl(11); // viewport id - for _ in 0..3 { - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - } - w.write_b(false); // perspective - w.write_bl(0); // silhouette wire count - w.write_b(true); // ACIS empty (2) - w.write_bl(0); // R2007+ unknown - let end = w.position_bits(); - - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let rec = decode_record(&mut c, Version::R2018, false).unwrap(); - assert_eq!(rec.tail.num_wires, 1); - assert_eq!(rec.tail.num_silhouettes, 1); - assert_eq!(rec.tail.point, None); - assert_eq!(c.position_bits(), end); - } - - #[test] - fn absurd_wire_count_is_rejected() { - let mut w = BitWriter::new(); - w.write_b(true); // wireframe present - w.write_b(false); // point present - w.write_bl(4); - w.write_b(true); // isolines present - w.write_bl(1_000_000); // wire count - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let err = decode_record(&mut c, Version::R2018, true).unwrap_err(); - match err { - Error::SectionMap(msg) => assert!(msg.contains("wire count"), "msg: {msg}"), - other => panic!("expected SectionMap, got {other:?}"), - } - } -} diff --git a/src/entities/ole2_frame.rs b/src/entities/ole2_frame.rs index 30dd4d7..379bad9 100644 --- a/src/entities/ole2_frame.rs +++ b/src/entities/ole2_frame.rs @@ -80,76 +80,3 @@ pub fn decode(c: &mut BitCursor<'_>) -> Result { mode, }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - - #[test] - fn roundtrip_ole2frame_small_blob() { - let mut w = BitWriter::new(); - w.write_bs(2); // ole_version - // Four-byte synthetic "blob" — a real OLE 2 header would start - // with the Compound Document magic (D0 CF 11 E0 A1 B1 1A E1), - // but this test only cares that decode preserves the bytes. - let blob = [0xD0u8, 0xCF, 0x11, 0xE0]; - w.write_bl(blob.len() as i32); - for b in blob { - w.write_rc(b); - } - w.write_bs(2); // embedded - w.write_rc(0); // mode - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let o = decode(&mut c).unwrap(); - assert_eq!(o.ole_version, 2); - assert_eq!(o.data, blob); - assert_eq!(o.oleobject_type, 2); - assert_eq!(o.mode, 0); - } - - #[test] - fn roundtrip_ole2frame_empty_blob() { - let mut w = BitWriter::new(); - w.write_bs(2); - w.write_bl(0); - w.write_bs(1); - w.write_rc(1); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let o = decode(&mut c).unwrap(); - assert!(o.data.is_empty()); - assert_eq!(o.oleobject_type, 1); - assert_eq!(o.mode, 1); - } - - #[test] - fn rejects_negative_data_length() { - let mut w = BitWriter::new(); - w.write_bs(2); - w.write_bl(-1); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let err = decode(&mut c).unwrap_err(); - assert!( - matches!(&err, Error::SectionMap(msg) if msg.contains("negative data_length")), - "err={err:?}" - ); - } - - #[test] - fn rejects_excessive_data_length() { - let mut w = BitWriter::new(); - w.write_bs(2); - // Claim 32 MiB — exceeds OLE2FRAME_MAX_DATA_BYTES (16 MiB). - w.write_bl(32 * 1024 * 1024); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let err = decode(&mut c).unwrap_err(); - assert!( - matches!(&err, Error::SectionMap(msg) if msg.contains("exceeds cap")), - "err={err:?}" - ); - } -} diff --git a/src/entities/polyface_mesh.rs b/src/entities/polyface_mesh.rs index 0973316..8f4a578 100644 --- a/src/entities/polyface_mesh.rs +++ b/src/entities/polyface_mesh.rs @@ -74,43 +74,3 @@ pub fn decode_record(c: &mut BitCursor<'_>, version: Version) -> Result, version: Version) -> Result { num_owned_objects, }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - - struct PmeshFields { - flags: u16, - md: u16, - nd: u16, - m: u16, - n: u16, - } - - fn write_pmesh(w: &mut BitWriter, f: &PmeshFields) { - w.write_bs_u(f.flags); - w.write_bs_u(0); // curve type - w.write_bs_u(f.m); - w.write_bs_u(f.n); - w.write_bs_u(f.md); - w.write_bs_u(f.nd); - } - - #[test] - fn roundtrip_open_mesh() { - let mut w = BitWriter::new(); - write_pmesh( - &mut w, - &PmeshFields { - flags: 0, - md: 6, - nd: 6, - m: 5, - n: 4, - }, - ); - let end = w.position_bits(); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let m = decode(&mut c, Version::R2000).unwrap(); - assert_eq!(m.flags, 0); - assert!(!m.is_closed_m()); - assert!(!m.is_closed_n()); - assert_eq!(m.m_density, 6); - assert_eq!(m.n_density, 6); - assert_eq!(m.m_vert_count, 5); - assert_eq!(m.n_vert_count, 4); - // The six shorts are the whole R2000 data-stream field list — the - // vertex-chain handles are not in it. - assert_eq!(c.position_bits(), end); - } - - #[test] - fn roundtrip_cylinder_closed_in_m() { - let mut w = BitWriter::new(); - write_pmesh( - &mut w, - &PmeshFields { - flags: flag_bits::CLOSED_M, - md: 8, - nd: 4, - m: 16, - n: 8, - }, - ); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let m = decode(&mut c, Version::R2000).unwrap(); - assert!(m.is_closed_m()); - assert!(!m.is_closed_n()); - assert_eq!(m.m_vert_count, 16); - assert_eq!(m.n_vert_count, 8); - } - - #[test] - fn roundtrip_torus_closed_both() { - let mut w = BitWriter::new(); - write_pmesh( - &mut w, - &PmeshFields { - flags: flag_bits::CLOSED_M | flag_bits::CLOSED_N, - md: 8, - nd: 8, - m: 24, - n: 12, - }, - ); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let m = decode(&mut c, Version::R2000).unwrap(); - assert!(m.is_closed_m()); - assert!(m.is_closed_n()); - assert_eq!(m.flags & 0x3, 0x3); - assert_eq!(m.m_vert_count, 24); - assert_eq!(m.n_vert_count, 12); - } - - #[test] - fn preserves_unknown_high_flag_bits() { - // Files in the wild sometimes carry extra bits we don't - // semantically interpret; round-tripping them verbatim is how - // the reader stays honest with unknown metadata. - let mut w = BitWriter::new(); - write_pmesh( - &mut w, - &PmeshFields { - flags: 0x8000, - md: 2, - nd: 3, - m: 7, - n: 5, - }, - ); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let m = decode(&mut c, Version::R2000).unwrap(); - assert_eq!(m.flags, 0x8000); - assert!(!m.is_closed_m()); - assert!(!m.is_closed_n()); - } -} diff --git a/src/entities/proxy_entity_passthrough.rs b/src/entities/proxy_entity_passthrough.rs index 4a8a4a1..2467866 100644 --- a/src/entities/proxy_entity_passthrough.rs +++ b/src/entities/proxy_entity_passthrough.rs @@ -104,88 +104,3 @@ pub fn decode(c: &mut BitCursor<'_>, version: Version) -> Result) -> Result { let direction = read_bd3(c)?; Ok(Ray { start, direction }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - - #[test] - fn roundtrip_ray() { - let mut w = BitWriter::new(); - w.write_bd(1.0); - w.write_bd(2.0); - w.write_bd(3.0); - w.write_bd(1.0); - w.write_bd(0.0); - w.write_bd(0.0); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let r = decode(&mut c).unwrap(); - assert_eq!( - r.start, - Point3D { - x: 1.0, - y: 2.0, - z: 3.0 - } - ); - assert_eq!( - r.direction, - Vec3D { - x: 1.0, - y: 0.0, - z: 0.0 - } - ); - } -} diff --git a/src/entities/region.rs b/src/entities/region.rs index 9299062..92cb1b5 100644 --- a/src/entities/region.rs +++ b/src/entities/region.rs @@ -67,78 +67,3 @@ pub fn decode_record( tail: Some(record.tail), }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - - #[test] - fn roundtrip_empty_region() { - let mut w = BitWriter::new(); - w.write_b(true); // ACIS empty - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let r = decode(&mut c).unwrap(); - assert!(r.acis_empty); - assert_eq!(r.version, None); - assert_eq!(r.sat_blob, None); - } - - #[test] - fn roundtrip_region_with_blob() { - let payload = b"400 0 1 0"; - let mut w = BitWriter::new(); - w.write_b(false); - w.write_b(false); // §20.4.41 unknown bit - w.write_bs_u(1); - w.write_bl(payload.len() as i32); - for b in payload { - w.write_rc(*b); - } - w.write_bl(0); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let r = decode(&mut c).unwrap(); - assert!(!r.acis_empty); - assert_eq!(r.version, Some(1)); - assert_eq!(r.sat_blob.as_deref(), Some(&payload[..])); - } - - /// The R2013+ data-store shape: no inline envelope, a wireframe - /// point, `num isolines = 4` and the 16-byte revision GUID. - #[test] - fn roundtrip_region_data_store_record() { - let guid = [0xABu8; 16]; - let mut w = BitWriter::new(); - w.write_b(true); // wireframe data present - w.write_b(true); // point present - w.write_bd(1.0); - w.write_bd(2.0); - w.write_bd(0.0); - w.write_bl(4); // num isolines - w.write_b(true); // isolines present - w.write_bl(0); // num wires - w.write_bl(0); // num silhouettes - w.write_b(true); // ACIS empty (2) - w.write_bl(0); // R2007+ unknown - w.write_b(true); - w.write_bb(0); - w.write_bb(0); - w.write_bb(0b10); - for b in guid { - w.write_rc(b); - } - w.write_bl(0); - let end = w.position_bits(); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let r = decode_record(&mut c, Version::R2018, true).unwrap(); - assert!(r.in_data_store); - assert!(r.acis_empty); - let fields = r.tail.unwrap(); - assert_eq!(fields.num_isolines, 4); - assert_eq!(fields.revision_guid, Some(guid)); - assert_eq!(c.position_bits(), end); - } -} diff --git a/src/entities/revolved_surface.rs b/src/entities/revolved_surface.rs index ed64dbf..f945f44 100644 --- a/src/entities/revolved_surface.rs +++ b/src/entities/revolved_surface.rs @@ -47,73 +47,3 @@ pub fn decode(c: &mut BitCursor<'_>) -> Result { sweep_angle, }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - use crate::entities::modeler::tests::write_sat_blob; - use std::f64::consts::PI; - - #[test] - fn roundtrip_full_revolution() { - let mut w = BitWriter::new(); - write_sat_blob( - &mut w, - &SatBlob { - empty: false, - version: 1, - bytes: b"R-SAT".to_vec(), - }, - ); - // axis origin at (0, 0, 0) - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - // axis direction +Z - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - // 360° - w.write_bd(2.0 * PI); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let s = decode(&mut c).unwrap(); - assert_eq!(s.axis_origin, Point3D::default()); - assert_eq!( - s.axis_direction, - Vec3D { - x: 0.0, - y: 0.0, - z: 1.0 - } - ); - assert!((s.sweep_angle - 2.0 * PI).abs() < 1e-12); - assert_eq!(s.sat.version, 1); - } - - #[test] - fn roundtrip_partial_sweep_with_empty_sat() { - let mut w = BitWriter::new(); - write_sat_blob( - &mut w, - &SatBlob { - empty: true, - version: 0, - bytes: Vec::new(), - }, - ); - w.write_bd(5.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(0.0); - w.write_bd(PI / 2.0); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let s = decode(&mut c).unwrap(); - assert!(s.sat.empty); - assert!((s.sweep_angle - PI / 2.0).abs() < 1e-12); - } -} diff --git a/src/entities/seqend.rs b/src/entities/seqend.rs index 51ff2a4..0e453ab 100644 --- a/src/entities/seqend.rs +++ b/src/entities/seqend.rs @@ -29,16 +29,3 @@ pub struct SeqEnd; pub fn decode(_c: &mut BitCursor<'_>) -> Result { Ok(SeqEnd) } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn seqend_consumes_nothing() { - let buf = [0xFFu8; 4]; - let mut c = BitCursor::new(&buf); - let _ = decode(&mut c).unwrap(); - assert_eq!(c.position_bits(), 0); - } -} diff --git a/src/entities/spline.rs b/src/entities/spline.rs index 05830fb..722dd1f 100644 --- a/src/entities/spline.rs +++ b/src/entities/spline.rs @@ -250,194 +250,3 @@ fn bounds_check(n: usize, field: &'static str, remaining_bits: usize) -> Result< Ok(()) } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - use crate::string_stream::tests::{bits_of, build_payload}; - - fn write_preamble(w: &mut BitWriter) { - w.write_bs_u(0); - w.write_b(false); - w.write_bb(0b10); - w.write_bl(0); - w.write_b(true); - w.write_b(false); - w.write_bs_u(0x0100); - w.write_bd(1.0); - w.write_bb(0b00); - w.write_bb(0b00); - w.write_bb(0b00); - w.write_rc(0); - w.write_b(false); - w.write_b(false); - w.write_b(false); - w.write_bs(0); - w.write_rc(0x1D); - } - - #[test] - fn roundtrip_fit_spline_pre_r2013() { - let mut w = BitWriter::new(); - w.write_bl(2); // scenario = fit - w.write_bl(3); // degree is a BL - w.write_bd(0.01); // fit tolerance - w.write_bd(1.0); - w.write_bd(0.0); - w.write_bd(0.0); // begin tangent - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(0.0); // end tangent - w.write_bl(3); // 3 fit points - for (x, y, z) in [(0.0, 0.0, 0.0), (1.0, 1.0, 0.0), (2.0, 0.0, 0.0)] { - w.write_bd(x); - w.write_bd(y); - w.write_bd(z); - } - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let s = decode(&mut c, Version::R2000).unwrap(); - let fit = s.fit.unwrap(); - assert_eq!(s.degree, 3); - assert_eq!(fit.fit_points.len(), 3); - assert_eq!(fit.tolerance, 0.01); - assert!(s.control.is_none()); - } - - #[test] - fn roundtrip_control_spline_pre_r2013() { - let mut w = BitWriter::new(); - w.write_bl(1); // control-based - w.write_bl(3); // degree - w.write_b(false); // not rational - w.write_b(false); // not closed - w.write_b(false); // not periodic - w.write_bd(1e-6); // knot tolerance - w.write_bd(1e-6); // control tolerance - w.write_bl(5); // 5 knots - w.write_bl(3); // 3 control points - w.write_b(false); // no weights - for k in [0.0, 0.0, 0.5, 1.0, 1.0] { - w.write_bd(k); - } - for (x, y, z) in [(0.0, 0.0, 0.0), (1.0, 2.0, 0.0), (2.0, 0.0, 0.0)] { - w.write_bd(x); - w.write_bd(y); - w.write_bd(z); - } - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let s = decode(&mut c, Version::R2000).unwrap(); - let ctl = s.control.unwrap(); - assert_eq!(ctl.knots.len(), 5); - assert_eq!(ctl.control_points.len(), 3); - assert!(ctl.weights.is_empty()); - } - - /// The R2018 shape measured on `sample_AC1032.dwg` handle `0x433`: - /// stored scenario `1`, spline flags `0`, knot parameter `15` - /// (Custom) — so the control branch is taken — with 8 knots and 4 - /// control points, and the whole record closing on its - /// data-stream boundary. - #[test] - fn r2018_control_spline_closes_on_the_boundary() { - let mut w = BitWriter::new(); - write_preamble(&mut w); - w.write_bl(1); // scenario - w.write_bl(0); // spline flags 1 - w.write_bl(15); // knot parameter = Custom - w.write_bl(3); // degree - w.write_b(false); // rational - w.write_b(false); // closed - w.write_b(false); // periodic - w.write_bd(1e-9); - w.write_bd(1e-10); - w.write_bl(8); // num knots - w.write_bl(4); // num control points - w.write_b(false); // no weights - for k in [0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0] { - w.write_bd(k); - } - for i in 0..4u8 { - w.write_bd(f64::from(i)); - w.write_bd(f64::from(i) * 2.0); - w.write_bd(0.0); - } - let body = bits_of(&w); - let payload = build_payload(&body, &[]); - let s = decode_modern_split_stream(&payload, 8, Version::R2018).unwrap(); - assert_eq!(s.scenario, 1); - assert_eq!(s.knot_param, Some(15)); - assert_eq!(s.degree, 3); - let ctl = s.control.expect("control form"); - assert_eq!(ctl.knots.len(), 8); - assert_eq!(ctl.control_points.len(), 4); - assert!(s.fit.is_none()); - } - - /// The R2018 shape measured on handle `0x434`: the same stored - /// scenario `1`, but spline flags `9` (method fit points + use knot - /// parameter) and knot parameter `2` (Uniform), which §20.4.40's - /// derivation turns into the *fit* branch. - #[test] - fn r2018_derived_scenario_takes_the_fit_branch() { - let mut w = BitWriter::new(); - write_preamble(&mut w); - w.write_bl(1); // stored scenario says control... - w.write_bl(9); // ...but the flags say method-fit-points - w.write_bl(2); // knot parameter = Uniform (not Custom) - w.write_bl(3); // degree - w.write_bd(1e-10); // fit tolerance - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(0.0); // begin tangent - w.write_bd(0.0); - w.write_bd(-1.0); - w.write_bd(0.0); // end tangent - w.write_bl(2); // 2 fit points - for (x, y) in [(250.0, 17.0), (259.0, 27.0)] { - w.write_bd(x); - w.write_bd(y); - w.write_bd(0.0); - } - let body = bits_of(&w); - let payload = build_payload(&body, &[]); - let s = decode_modern_split_stream(&payload, 8, Version::R2018).unwrap(); - assert_eq!(s.scenario, 1); - assert_eq!(s.flag1, Some(9)); - let fit = s.fit.expect("fit form"); - assert_eq!(fit.fit_points.len(), 2); - assert_eq!(fit.tolerance, 1e-10); - assert!(s.control.is_none()); - } - - /// A field list one bit short of the boundary must be rejected, - /// not returned. - #[test] - fn misaligned_field_list_errors() { - let mut w = BitWriter::new(); - write_preamble(&mut w); - w.write_bl(1); - w.write_bl(0); - w.write_bl(15); - w.write_bl(3); - w.write_b(false); - w.write_b(false); - w.write_b(false); - w.write_bd(1e-9); - w.write_bd(1e-10); - w.write_bl(0); // num knots - w.write_bl(0); // num control points - w.write_b(false); - let mut body = bits_of(&w); - body.push(false); // one bit the field list will not consume - let payload = build_payload(&body, &[]); - let err = decode_modern_split_stream(&payload, 8, Version::R2018) - .expect_err("an extra bit must be rejected"); - assert!( - matches!(&err, Error::SectionMap(m) if m.contains("SPLINE data fields ended")), - "err={err:?}" - ); - } -} diff --git a/src/entities/swept_surface.rs b/src/entities/swept_surface.rs index c7a39c4..d5eb4bc 100644 --- a/src/entities/swept_surface.rs +++ b/src/entities/swept_surface.rs @@ -91,58 +91,3 @@ pub fn decode(c: &mut BitCursor<'_>) -> Result { align_option, }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - use crate::entities::modeler::tests::write_sat_blob; - - #[test] - fn roundtrip_swept_surface_with_handle() { - let mut w = BitWriter::new(); - write_sat_blob( - &mut w, - &SatBlob { - empty: false, - version: 1, - bytes: b"SW".to_vec(), - }, - ); - // Soft-owner handle (code 2), value 0x42. - w.write_handle(2, 0x42); - w.write_bb(1); // Normal alignment - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let s = decode(&mut c).unwrap(); - assert_eq!(s.sat.version, 1); - assert_eq!(s.path_handle.code, 2); - assert_eq!(s.path_handle.value, 0x42); - assert_eq!(s.align_option, AlignOption::Normal); - } - - #[test] - fn rejects_bad_alignment_enum() { - // The BB read at the end of decode() will only ever yield - // 0..=3, so the enum converter cannot be driven to an - // out-of-range value from decode(). Still, exercise the - // converter directly so the error path is covered. - assert!(AlignOption::from_bb(4).is_err()); - assert!(matches!( - AlignOption::from_bb(4).unwrap_err(), - Error::SectionMap(_) - )); - } - - #[test] - fn align_option_roundtrip() { - for opt in [ - AlignOption::NoAlignment, - AlignOption::Normal, - AlignOption::Translate, - AlignOption::ForcedAlignment, - ] { - assert_eq!(AlignOption::from_bb(opt.to_bb()).unwrap(), opt); - } - } -} diff --git a/src/entities/three_d_solid.rs b/src/entities/three_d_solid.rs index 7e7e6d8..c7b2ec6 100644 --- a/src/entities/three_d_solid.rs +++ b/src/entities/three_d_solid.rs @@ -262,95 +262,3 @@ fn read_sab_file(c: &mut BitCursor<'_>) -> Result> { blob.push(c.read_rc()?); } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - - #[test] - fn roundtrip_empty_body() { - // acis_empty = true ⇒ no version, no blocks. - let mut w = BitWriter::new(); - w.write_b(true); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let s = decode(&mut c).unwrap(); - assert!(s.acis_empty); - assert_eq!(s.version, None); - assert_eq!(s.sat_blob, None); - assert_eq!(s.tail, None); - } - - #[test] - fn roundtrip_single_block() { - // acis_empty=false, unknown, version=1, one 9-byte block, stop. - // "400 0 1 0" is the header line every SAT stream opens with. - let payload: [u8; 9] = *b"400 0 1 0"; - let mut w = BitWriter::new(); - w.write_b(false); - w.write_b(false); - w.write_bs_u(1); - w.write_bl(payload.len() as i32); - for b in payload { - w.write_rc(b); - } - w.write_bl(0); // terminating block size - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let s = decode(&mut c).unwrap(); - assert!(!s.acis_empty); - assert_eq!(s.version, Some(1)); - assert_eq!(s.sat_blob.as_deref(), Some(&payload[..])); - } - - #[test] - fn roundtrip_multi_block() { - // Two blocks that together form "hello, world!". - let mut w = BitWriter::new(); - w.write_b(false); - w.write_b(true); - w.write_bs_u(1); - w.write_bl(7); - for b in b"hello, " { - w.write_rc(*b); - } - w.write_bl(6); - for b in b"world!" { - w.write_rc(*b); - } - w.write_bl(0); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let s = decode(&mut c).unwrap(); - assert_eq!(s.version, Some(1)); - assert_eq!(s.sat_blob.as_deref(), Some(&b"hello, world!"[..])); - } - - #[test] - fn block_size_over_remaining_rejected() { - // Claim 1_000_000 bytes in a single block but provide almost - // nothing. Must return SectionMap, not allocate 1M. - let mut w = BitWriter::new(); - w.write_b(false); - w.write_b(false); - w.write_bs_u(1); - w.write_bl(1_000_000); - // No payload follows — cursor runs out. - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let err = decode(&mut c).unwrap_err(); - match err { - Error::SectionMap(msg) => { - assert!(msg.contains("exceeds remaining cursor"), "msg: {msg}"); - } - other => panic!("expected SectionMap, got {other:?}"), - } - } - - #[test] - fn max_sat_blob_cap_constant() { - // Compile-time sanity: 32 MiB. - assert_eq!(MAX_SAT_BLOB_BYTES, 32 * 1024 * 1024); - } -} diff --git a/src/entities/underlay.rs b/src/entities/underlay.rs index 90bb689..0b6080f 100644 --- a/src/entities/underlay.rs +++ b/src/entities/underlay.rs @@ -156,114 +156,3 @@ pub fn decode(c: &mut BitCursor<'_>, kind: UnderlayKind) -> Result { clip_polygon, }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - - fn write_minimal_underlay(flags: u8, clip_verts: &[(f64, f64)]) -> Vec { - let mut w = BitWriter::new(); - // normal - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - // insertion_point - w.write_bd(1.0); - w.write_bd(2.0); - w.write_bd(0.0); - // rotation - w.write_bd(0.0); - // scale - w.write_bd(1.0); - w.write_bd(1.0); - w.write_bd(1.0); - // flags / contrast / fade - w.write_rc(flags); - w.write_rc(75); - w.write_rc(20); - // clip polygon - w.write_bs(clip_verts.len() as i16); - for (x, y) in clip_verts { - w.write_bd(*x); - w.write_bd(*y); - } - w.into_bytes() - } - - #[test] - fn roundtrip_pdf_underlay_with_rect_clip() { - let bytes = write_minimal_underlay(0x03, &[(0.0, 0.0), (10.0, 10.0)]); - let mut c = BitCursor::new(&bytes); - let u = decode(&mut c, UnderlayKind::Pdf).unwrap(); - assert_eq!(u.kind, UnderlayKind::Pdf); - assert_eq!( - u.insertion_point, - Point3D { - x: 1.0, - y: 2.0, - z: 0.0 - } - ); - assert_eq!(u.contrast, 75); - assert_eq!(u.fade, 20); - assert_eq!(u.clip_polygon.len(), 2); - assert!(u.is_clip_on()); - assert!(u.is_underlay_on()); - assert!(!u.is_monochrome()); - assert!(!u.is_adjust_for_background()); - } - - #[test] - fn roundtrip_dwf_underlay_no_clip() { - let bytes = write_minimal_underlay(0x02, &[]); - let mut c = BitCursor::new(&bytes); - let u = decode(&mut c, UnderlayKind::Dwf).unwrap(); - assert_eq!(u.kind, UnderlayKind::Dwf); - assert!(u.clip_polygon.is_empty()); - assert!(!u.is_clip_on()); - assert!(u.is_underlay_on()); - } - - #[test] - fn roundtrip_dgn_underlay_preserves_flags() { - let bytes = write_minimal_underlay(0x0F, &[(0.0, 0.0)]); - let mut c = BitCursor::new(&bytes); - let u = decode(&mut c, UnderlayKind::Dgn).unwrap(); - assert_eq!(u.kind, UnderlayKind::Dgn); - assert_eq!(u.flags, 0x0F); - assert!(u.is_monochrome()); - assert!(u.is_adjust_for_background()); - } - - #[test] - fn rejects_excessive_clip_verts() { - // Write a payload that *claims* far more verts than fit, via a - // BS that encodes a large positive value. We stop after the BS - // since decode should reject before reading the polygon. - let mut w = BitWriter::new(); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(1.0); - w.write_bd(1.0); - w.write_rc(0); - w.write_rc(0); - w.write_rc(0); - // A BS that reads as 32767 (max i16). 32767 BD2s need ~16 million - // bits of payload but only a handful remain. - w.write_bs(32767); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let err = decode(&mut c, UnderlayKind::Pdf).unwrap_err(); - assert!( - matches!(&err, Error::SectionMap(msg) if msg.contains("UNDERLAY clip verts")), - "err={err:?}" - ); - } -} diff --git a/src/entities/vertex.rs b/src/entities/vertex.rs index c62c136..05146c3 100644 --- a/src/entities/vertex.rs +++ b/src/entities/vertex.rs @@ -154,91 +154,3 @@ pub fn decode_pface_face(c: &mut BitCursor<'_>) -> Result { } Ok(VertexPfaceFace { vertex_indices }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - - /// The measured VERTEX_3D shape: flag `0x20` then a point whose z - /// defaults, in exactly 142 bits. - #[test] - fn roundtrip_measured_vertex_3d() { - let mut w = BitWriter::new(); - w.write_rc(0x20); - w.write_bd(232.60172074430375); - w.write_bd(0.8926935903469939); - w.write_bd(0.0); - assert_eq!(w.position_bits(), 142); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let v = decode_point(&mut c).unwrap(); - assert_eq!(v.flag, 0x20); - assert_eq!(v.location.z, 0.0); - assert_eq!(c.position_bits(), 142); - } - - /// The measured VERTEX_PFACE_FACE shape: four `BS`, one negative - /// (invisible edge), in exactly 48 bits. - #[test] - fn roundtrip_measured_pface_face() { - let mut w = BitWriter::new(); - for value in [1i16, 2, 3, -4] { - w.write_bs(value); - } - assert_eq!(w.position_bits(), 48); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let f = decode_pface_face(&mut c).unwrap(); - assert_eq!(f.vertex_indices, [1, 2, 3, -4]); - assert_eq!(c.position_bits(), 48); - } - - #[test] - fn roundtrip_simple_vertex_r2000() { - let mut w = BitWriter::new(); - w.write_rc(0x00); // no flags - w.write_bd(1.0); - w.write_bd(2.0); - w.write_bd(3.0); - w.write_bd(0.0); // start width - w.write_bd(0.0); // end width - w.write_bd(0.0); // bulge - w.write_bd(0.0); // tangent dir: §20.4.11 stores it on every vertex - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let v = decode(&mut c, Version::R2000).unwrap(); - assert_eq!( - v.location, - Point3D { - x: 1.0, - y: 2.0, - z: 3.0 - } - ); - assert!(v.vertex_id.is_none()); - assert_eq!(v.tangent_direction, Some(0.0)); - } - - #[test] - fn roundtrip_vertex_with_tangent_r2018() { - let mut w = BitWriter::new(); - w.write_rc(0x02); // tangent present - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); // start width - w.write_bd(2.0); // end width - w.write_bd(0.5); // bulge - w.write_bl(42); // vertex id - w.write_bd(std::f64::consts::FRAC_PI_4); // tangent - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let v = decode(&mut c, Version::R2018).unwrap(); - assert_eq!(v.vertex_id, Some(42)); - assert!((v.tangent_direction.unwrap() - std::f64::consts::FRAC_PI_4).abs() < 1e-12); - assert_eq!(v.bulge, 0.5); - assert_eq!(v.start_width, 1.0); - assert_eq!(v.end_width, 2.0); - } -} diff --git a/src/entities/wipeout.rs b/src/entities/wipeout.rs index 6d2abef..8512b8e 100644 --- a/src/entities/wipeout.rs +++ b/src/entities/wipeout.rs @@ -42,55 +42,3 @@ pub type Wipeout = super::image::Image; pub fn decode(c: &mut BitCursor<'_>, version: Version) -> Result { super::image::decode(c, version) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - use crate::entities::image::ClipBoundary; - - /// The measured `sample_AC1032.dwg` WIPEOUT body: a polygon clip - /// with an explicit vertex count, decoded through the IMAGE layout. - #[test] - fn decodes_the_measured_polygon_wipeout_body() { - let mut w = BitWriter::new(); - w.write_bl(0); // class_version - w.write_bd(271.9211288808192); // insertion point - w.write_bd(3.988123351850959); - w.write_bd(0.0); - w.write_bd(31.707742926072683); // u vector - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(0.0); // v vector - w.write_bd(31.707742926072683); - w.write_bd(0.0); - w.write_rd(1.0); // image size - w.write_rd(1.0); - w.write_bs(7); // display flags - w.write_b(true); // clipping - w.write_rc(50); // brightness - w.write_rc(50); // contrast - w.write_rc(0); // fade - w.write_b(false); // clip mode (R2010+) - w.write_bs(2); // polygon boundary - w.write_bl(4); // vertex count - for (x, y) in [(0.0f64, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)] { - w.write_rd(x); - w.write_rd(y); - } - let end = w.position_bits(); - - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let wp = decode(&mut c, Version::R2018).unwrap(); - assert_eq!(wp.brightness, 50); - assert_eq!(wp.contrast, 50); - assert_eq!(wp.fade, 0); - assert!(wp.clipping); - match &wp.clip_boundary { - ClipBoundary::Polygon(pts) => assert_eq!(pts.len(), 4), - other => panic!("expected a polygon boundary, got {other:?}"), - } - assert_eq!(c.position_bits(), end); - } -} diff --git a/src/entities/xline.rs b/src/entities/xline.rs index 61fc4fb..a2c3c8c 100644 --- a/src/entities/xline.rs +++ b/src/entities/xline.rs @@ -20,25 +20,3 @@ pub fn decode(c: &mut BitCursor<'_>) -> Result { let direction = read_bd3(c)?; Ok(XLine { point, direction }) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - - #[test] - fn roundtrip_xline() { - let mut w = BitWriter::new(); - w.write_bd(5.0); - w.write_bd(5.0); - w.write_bd(0.0); - w.write_bd(0.0); - w.write_bd(1.0); - w.write_bd(0.0); - let bytes = w.into_bytes(); - let mut c = BitCursor::new(&bytes); - let x = decode(&mut c).unwrap(); - assert_eq!(x.point.x, 5.0); - assert_eq!(x.direction.y, 1.0); - } -}