Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 0 additions & 91 deletions src/objects/acad_group.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,94 +116,3 @@ pub(crate) fn decode_object(
split.finish("ACAD_GROUP")?;
Ok(out)
}

#[cfg(test)]
mod tests {
use super::*;
use crate::bitwriter::BitWriter;

fn encode_tv_r2000(w: &mut BitWriter, s: &[u8]) {
w.write_bs_u(s.len() as u16);
for b in s {
w.write_rc(*b);
}
}

#[test]
fn roundtrip_empty_group() {
let mut w = BitWriter::new();
encode_tv_r2000(&mut w, b"MyGroup");
w.write_bs(0); // named
w.write_bs(1); // selectable
w.write_bl(0);
let bytes = w.into_bytes();
let mut c = BitCursor::new(&bytes);
let g = decode(&mut c, Version::R2000).unwrap();
assert_eq!(g.name, "MyGroup");
assert!(!g.is_unnamed());
assert!(g.is_selectable());
assert_eq!(g.num_members, 0);
}

#[test]
fn roundtrip_populated_group() {
let mut w = BitWriter::new();
encode_tv_r2000(&mut w, b"*A1");
w.write_bs(2); // unnamed (AutoCAD-generated)
w.write_bs(0); // not selectable as a whole
w.write_bl(2);
let bytes = w.into_bytes();
let mut c = BitCursor::new(&bytes);
let g = decode(&mut c, Version::R2000).unwrap();
assert_eq!(g.name, "*A1");
assert!(g.is_unnamed());
assert!(!g.is_selectable());
assert_eq!(g.num_members, 2);
}

/// The exact field widths of `sample_AC1032.dwg`'s two GROUP
/// records: three 10-bit fields reading 2, 1 and 6.
#[test]
fn r2018_split_stream_group_matches_the_measured_widths() {
let mut body = modern::tests::r2018_object_prefix(1);
body.write_bs(2); // unnamed
body.write_bs(1); // selectable
body.write_bl(6); // member count
let bits = crate::string_stream::tests::bits_of(&body);
assert_eq!(bits.len(), 14 + 30, "measured budget is 30 bits of body");
let payload = crate::string_stream::tests::build_payload(&bits, &[""]);
let g = decode_object(&payload, 8, None, Version::R2018).unwrap();
assert_eq!(g.name, "");
assert_eq!(g.unnamed, 2);
assert_eq!(g.selectable, 1);
assert_eq!(g.num_members, 6);
}

/// Two single-bit flags cannot fill the measured budget — the
/// pre-fix field list must now be rejected, not silently accepted.
#[test]
fn r2018_split_stream_group_rejects_the_old_boolean_field_list() {
let mut body = modern::tests::r2018_object_prefix(1);
body.write_b(true);
body.write_b(true);
body.write_bl(6);
let bits = crate::string_stream::tests::bits_of(&body);
let payload = crate::string_stream::tests::build_payload(&bits, &[""]);
assert!(decode_object(&payload, 8, None, Version::R2018).is_err());
}

#[test]
fn rejects_excessive_handle_count() {
// Synthesize a stream where num_handles claims a count past the cap.
let mut w = BitWriter::new();
encode_tv_r2000(&mut w, b"G");
w.write_bs(0);
w.write_bs(1);
// BL 0b00 tag then 32-bit LE = 200_001
w.write_bl((MAX_GROUP_HANDLES + 1) as i32);
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("ACAD_GROUP")));
}
}
163 changes: 0 additions & 163 deletions src/objects/acad_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,166 +304,3 @@ pub fn decode_object(
trailing_strings,
})
}

#[cfg(test)]
mod tests {
use super::*;
use crate::bitwriter::BitWriter;
use crate::objects::acad_plot_settings::tests::write_split_stream_block;

fn write_bd3(w: &mut BitWriter, p: Point3D) {
w.write_bd(p.x);
w.write_bd(p.y);
w.write_bd(p.z);
}

fn write_rd2(w: &mut BitWriter, p: Point2D) {
w.write_rd(p.x);
w.write_rd(p.y);
}

const X_AXIS: Point3D = Point3D {
x: 1.0,
y: 0.0,
z: 0.0,
};
const Y_AXIS: Point3D = Point3D {
x: 0.0,
y: 1.0,
z: 0.0,
};

/// Build the LAYOUT half of the body, with the `TV` layout name
/// left to the string stream.
fn write_layout_block(w: &mut BitWriter, tab_order: i32, limmax: Point2D, viewports: i32) {
w.write_bl(tab_order);
w.write_bs(1); // flags
write_bd3(w, Point3D::default()); // ucs_origin
write_rd2(w, Point2D::default()); // limmin
write_rd2(w, limmax);
write_bd3(w, Point3D::default()); // insertion_point
write_bd3(w, X_AXIS);
write_bd3(w, Y_AXIS);
w.write_bd(0.0); // elevation
w.write_bs(0); // ucs_ortho_view_type
write_bd3(w, Point3D::default()); // extent_min
write_bd3(
w,
Point3D {
x: 3.0,
y: 4.0,
z: 0.0,
},
); // extent_max
w.write_bl(viewports);
}

fn build_r2018(name: &str, tab_order: i32, limmax: Point2D, viewports: i32) -> Vec<u8> {
let mut body = modern::tests::r2018_object_prefix(1);
write_split_stream_block(&mut body);
write_layout_block(&mut body, tab_order, limmax, viewports);
let bits = crate::string_stream::tests::bits_of(&body);
crate::string_stream::tests::build_payload(
&bits,
&[
"",
"none_device",
"ANSI_A_(8.50_x_11.00_Inches)",
"acad.ctb",
name,
],
)
}

#[test]
fn r2018_split_stream_layout_closes_on_its_string_stream() {
let payload = build_r2018("Layout1", 1, Point2D { x: 10.75, y: 8.25 }, 2);
let l = decode_object(&payload, 8, None, Version::R2018).unwrap();
assert_eq!(l.layout_name, "Layout1");
assert_eq!(l.tab_order, 1);
assert_eq!(l.flags, 1);
assert_eq!(l.viewport_count, 2);
assert!(!l.is_model_space());
assert_eq!(l.ucs_x_axis, X_AXIS);
assert_eq!(l.ucs_y_axis, Y_AXIS);
assert!((l.limits_width() - 10.75).abs() < 1e-12);
assert!((l.limits_height() - 8.25).abs() < 1e-12);
assert!((l.extents_diagonal() - 5.0).abs() < 1e-12);
assert!(l.trailing_strings.is_empty());
// The embedded plot-settings block round-trips too.
assert_eq!(l.plot_settings.printer_config_name, "none_device");
assert_eq!(l.plot_settings.paper_size, "ANSI_A_(8.50_x_11.00_Inches)");
assert!((l.plot_settings.paper_width - 215.9).abs() < 1e-12);
assert!((l.plot_settings.paper_height - 279.4).abs() < 1e-12);
assert!((l.plot_settings.margin_left - 6.35).abs() < 1e-12);
assert_eq!(l.plot_settings.shade_plot_custom_dpi, 300);
}

#[test]
fn model_layout_is_recognised() {
let payload = build_r2018("Model", 0, Point2D { x: 12.0, y: 9.0 }, 0);
let l = decode_object(&payload, 8, None, Version::R2018).unwrap();
assert!(l.is_model_space());
assert_eq!(l.viewport_count, 0);
assert!((l.limits_width() - 12.0).abs() < 1e-12);
}

/// `viewport_count` is a `BL`: reading the same body with a 32-bit
/// `RL` there would overrun the string-stream start, so the
/// boundary check must reject a body that carries an `RL`.
#[test]
fn an_rl_sized_viewport_count_does_not_close() {
let mut body = modern::tests::r2018_object_prefix(1);
write_split_stream_block(&mut body);
body.write_bl(1); // tab_order
body.write_bs(1); // flags
write_bd3(&mut body, Point3D::default());
write_rd2(&mut body, Point2D::default());
write_rd2(&mut body, Point2D { x: 1.0, y: 1.0 });
write_bd3(&mut body, Point3D::default());
write_bd3(&mut body, X_AXIS);
write_bd3(&mut body, Y_AXIS);
body.write_bd(0.0);
body.write_bs(0);
write_bd3(&mut body, Point3D::default());
write_bd3(&mut body, Point3D::default());
body.write_rl(2); // an RL viewport count instead of a BL
let bits = crate::string_stream::tests::bits_of(&body);
let payload = crate::string_stream::tests::build_payload(
&bits,
&["", "none_device", "A4", "", "Layout1"],
);
assert!(decode_object(&payload, 8, None, Version::R2018).is_err());
}

/// The plot-settings block is not optional: a body that omits it
/// leaves the field list short and the boundary check has to
/// reject it.
#[test]
fn a_body_without_the_plot_settings_block_is_rejected() {
let mut body = modern::tests::r2018_object_prefix(1);
write_layout_block(&mut body, 0, Point2D::default(), 0);
let bits = crate::string_stream::tests::bits_of(&body);
let payload = crate::string_stream::tests::build_payload(&bits, &["", "", "", "", "Model"]);
assert!(decode_object(&payload, 8, None, Version::R2018).is_err());
}

#[test]
fn uninitialised_extents_report_a_zero_diagonal() {
let l = AcadLayout {
extent_min: Point3D {
x: 1e20,
y: 1e20,
z: 1e20,
},
extent_max: Point3D {
x: -1e20,
y: -1e20,
z: -1e20,
},
..AcadLayout::default()
};
assert!(l.has_uninitialised_extents());
assert_eq!(l.extents_diagonal(), 0.0);
}
}
49 changes: 1 addition & 48 deletions src/objects/acad_material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ pub(crate) fn decode_object(

/// The strings and data-field budget of an ACAD_MATERIAL record, as
/// [`read_strings`] measured them before the field list was derived.
/// [`Material`] supersedes it; it is kept with its tests.
/// [`Material`] supersedes it; it stays as public API.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AcadMaterial {
/// Material name — `ByLayer`, `ByBlock` and `Global` in every
Expand Down Expand Up @@ -336,50 +336,3 @@ pub fn read_strings(
data_field_bits,
})
}

#[cfg(test)]
mod tests {
use super::*;
use crate::bitwriter::BitWriter;

#[test]
fn r2018_split_stream_material_reads_its_names_from_the_string_stream() {
let mut body = modern::tests::r2018_object_prefix(1);
// Stand-in for the undecoded data fields.
for _ in 0..40 {
body.write_b(false);
}
let bits = crate::string_stream::tests::bits_of(&body);
let payload =
crate::string_stream::tests::build_payload(&bits, &["Global", "", "", "", "", "", ""]);
let material = read_strings(&payload, 8, None, Version::R2018).unwrap();
assert_eq!(material.name, "Global");
assert_eq!(material.description, "");
assert_eq!(material.extra_strings.len(), 5);
assert_eq!(material.data_field_bits, Some(40));
}

#[test]
fn inline_layout_reads_its_names_from_the_data_cursor() {
let mut w = BitWriter::new();
w.write_bs_u(0); // EED terminator
w.write_bl(1); // num_reactors
w.write_b(true); // no xdictionary (R2004+)
w.write_bs_u(8);
for b in b"ByLayer\0" {
w.write_rc(*b);
}
w.write_bs_u(0); // empty description
let after_names = w.position_bits();
for _ in 0..24 {
w.write_b(false);
}
let end = w.position_bits();
let bytes = w.into_bytes();
let material = read_strings(&bytes, 0, Some(end), Version::R2004).unwrap();
assert_eq!(material.name, "ByLayer");
assert_eq!(material.description, "");
assert!(material.extra_strings.is_empty());
assert_eq!(material.data_field_bits, Some(end - after_names));
}
}
Loading
Loading