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
210 changes: 0 additions & 210 deletions src/bitwriter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,213 +349,3 @@ impl BitWriter {
}
}
}

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

/// Round-trip: write a set of primitives, read them back, assert
/// equality.
#[test]
fn roundtrip_b_bb_3b() {
let mut w = BitWriter::new();
w.write_b(true);
w.write_b(false);
w.write_bb(2);
w.write_3b(6);
let bytes = w.into_bytes();
let mut c = BitCursor::new(&bytes);
assert!(c.read_b().unwrap());
assert!(!c.read_b().unwrap());
assert_eq!(c.read_bb().unwrap(), 2);
assert_eq!(c.read_3b().unwrap(), 6);
}

#[test]
fn roundtrip_bs_specials() {
for v in [0i16, 256, 1, -1, 100, -100, 12345, -12345] {
let mut w = BitWriter::new();
w.write_bs(v);
let bytes = w.into_bytes();
let mut c = BitCursor::new(&bytes);
assert_eq!(c.read_bs().unwrap(), v, "v={v}");
}
}

#[test]
fn roundtrip_bl_specials() {
for v in [0i32, 1, -1, 255, 256, 100_000, -100_000, i32::MAX, i32::MIN] {
let mut w = BitWriter::new();
w.write_bl(v);
let bytes = w.into_bytes();
let mut c = BitCursor::new(&bytes);
assert_eq!(c.read_bl().unwrap(), v, "v={v}");
}
}

#[test]
fn roundtrip_bd_specials() {
for v in [0.0f64, 1.0, 2.5, -42.125, 1e100, -1e-100] {
let mut w = BitWriter::new();
w.write_bd(v);
let bytes = w.into_bytes();
let mut c = BitCursor::new(&bytes);
let got = c.read_bd().unwrap();
if v == 0.0 {
assert_eq!(got, 0.0);
} else if v == 1.0 {
assert_eq!(got, 1.0);
} else {
assert!((got - v).abs() < 1e-10, "v={v} got={got}");
}
}
}

#[test]
fn roundtrip_dd_default_and_full() {
for (default, v) in [
(0.0f64, 0.0f64),
(1.0, 1.0),
(12.5, -42.125),
(-1e-100, 1e100),
] {
let mut w = BitWriter::new();
w.write_dd(default, v);
let bytes = w.into_bytes();
let mut c = BitCursor::new(&bytes);
assert_eq!(c.read_dd(default).unwrap(), v);
}
}

#[test]
fn roundtrip_rc_rs_rl_rd() {
let mut w = BitWriter::new();
w.write_rc(0xAB);
w.write_rs(-12345);
w.write_rl(0xDEAD_BEEF);
w.write_rd(42.5);
let bytes = w.into_bytes();
let mut c = BitCursor::new(&bytes);
assert_eq!(c.read_rc().unwrap(), 0xAB);
assert_eq!(c.read_rs().unwrap(), -12345);
assert_eq!(c.read_rl().unwrap(), 0xDEAD_BEEF);
assert_eq!(c.read_rd().unwrap(), 42.5);
}

#[test]
fn roundtrip_handle() {
let mut w = BitWriter::new();
w.write_handle(5, 0x0F);
let bytes = w.into_bytes();
let mut c = BitCursor::new(&bytes);
let h = c.read_handle().unwrap();
assert_eq!(h.code, 5);
assert_eq!(h.counter, 1);
assert_eq!(h.value, 0x0F);
}

// ================================================================
// Regression tests for correctness bugs surfaced by the external
// release-readiness audit.
// ================================================================

/// `position_bits` must return `N * 8` after exactly `N` whole
/// bytes have been written, not `(N - 1) * 8`.
#[test]
fn position_bits_after_whole_bytes() {
let mut w = BitWriter::new();
assert_eq!(w.position_bits(), 0);
w.write_bits(0xAB, 8);
assert_eq!(w.position_bits(), 8, "after 1 byte");
w.write_bits(0xCD, 8);
assert_eq!(w.position_bits(), 16, "after 2 bytes");
w.write_bits(0x1, 1);
assert_eq!(w.position_bits(), 17, "after 2 bytes + 1 bit");
// Fill out the current byte.
w.write_bits(0, 7);
assert_eq!(w.position_bits(), 24, "after 3 bytes (boundary)");
}

/// `write_mc` must not overflow on `i64::MIN`. The two's-complement
/// negation `-i64::MIN` is unrepresentable as `i64`; the encoder
/// must use `unsigned_abs` instead.
#[test]
fn write_mc_i64_min_does_not_overflow() {
let mut w = BitWriter::new();
w.write_mc(i64::MIN).unwrap();
let bytes = w.into_bytes();
let mut c = BitCursor::new(&bytes);
// Round-trip must preserve the value.
assert_eq!(c.read_mc().unwrap(), i64::MIN);
}

#[test]
fn write_mc_i64_max_roundtrips() {
let mut w = BitWriter::new();
w.write_mc(i64::MAX).unwrap();
let bytes = w.into_bytes();
let mut c = BitCursor::new(&bytes);
assert_eq!(c.read_mc().unwrap(), i64::MAX);
}

/// `try_write_3b` returns `Err(Error::Invalid3B)` for values above
/// `7` instead of panicking.
#[test]
fn try_write_3b_rejects_invalid_values() {
for bad in [8u8, 9, 15, 128, 255] {
let mut w = BitWriter::new();
let err = w.try_write_3b(bad).unwrap_err();
assert!(
matches!(err, crate::error::Error::Invalid3B { value } if value == bad),
"bad={bad} err={err:?}"
);
}
}

#[test]
fn try_write_3b_accepts_representable_values() {
for good in 0u8..=7 {
let mut w = BitWriter::new();
assert!(w.try_write_3b(good).is_ok(), "good={good}");
}
}

/// BLL must round-trip across the full byte-length spectrum the
/// three-bit count can represent (0..=7 bytes).
#[test]
fn bll_roundtrip_spans_representable_range() {
let values: [u64; 11] = [
0,
1,
0xFF,
0x100,
0xFFFF,
0x10000,
0xFFFF_FFFF,
0xFFFF_FFFF_FFFF,
1u64 << 40,
1u64 << 48,
(1u64 << 56) - 1,
];
for v in values {
let mut w = BitWriter::new();
w.write_bll(v)
.unwrap_or_else(|e| panic!("write_bll({v}): {e}"));
let bytes = w.into_bytes();
let mut c = BitCursor::new(&bytes);
let got = c.read_bll().unwrap();
assert_eq!(got, v, "roundtrip mismatch for v={v:#018X}");
}
}

#[test]
fn bll_rejects_values_requiring_more_than_56_bits() {
let mut w = BitWriter::new();
let res = w.write_bll(1u64 << 56);
assert!(
matches!(res, Err(crate::error::Error::BllOverflow { .. })),
"res={res:?}"
);
}
}
167 changes: 0 additions & 167 deletions src/element_encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,170 +150,3 @@ impl ElementEncoder for Point {
Ok(())
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::bitcursor::BitCursor;
use crate::entities::{Point3D, arc, circle, line, point};

fn default_extrusion() -> Vec3D {
Vec3D {
x: 0.0,
y: 0.0,
z: 1.0,
}
}

// -------- LINE round-trips --------

#[test]
fn line_2d_roundtrips_via_encoder_and_decoder() {
let original = Line {
start: Point3D {
x: 1.0,
y: 2.0,
z: 0.0,
},
end: Point3D {
x: 6.0,
y: 5.0,
z: 0.0,
},
thickness: 0.0,
extrusion: default_extrusion(),
is_2d: true,
};
let mut w = BitWriter::new();
original.encode(&mut w, Version::R2018).unwrap();
let bytes = w.into_bytes();
let mut c = BitCursor::new(&bytes);
let round = line::decode(&mut c).unwrap();
assert_eq!(round, original);
}

#[test]
fn line_3d_with_explicit_thickness_and_extrusion_roundtrips() {
let original = Line {
start: Point3D {
x: 1.0,
y: 3.0,
z: 5.0,
},
end: Point3D {
x: 3.0,
y: 7.0,
z: 11.0,
},
thickness: 2.5,
extrusion: Vec3D {
x: 0.5,
y: 0.25,
z: 0.75,
},
is_2d: false,
};
let mut w = BitWriter::new();
original.encode(&mut w, Version::R2018).unwrap();
let bytes = w.into_bytes();
let mut c = BitCursor::new(&bytes);
let round = line::decode(&mut c).unwrap();
assert_eq!(round, original);
}

// -------- CIRCLE round-trip --------

#[test]
fn circle_default_thickness_and_extrusion_roundtrips() {
let original = Circle {
center: Point3D {
x: 10.0,
y: 20.0,
z: 0.0,
},
radius: 5.0,
thickness: 0.0,
extrusion: default_extrusion(),
};
let mut w = BitWriter::new();
original.encode(&mut w, Version::R2018).unwrap();
let bytes = w.into_bytes();
let mut c = BitCursor::new(&bytes);
let round = circle::decode(&mut c).unwrap();
assert_eq!(round, original);
}

// -------- ARC round-trip --------

#[test]
fn arc_quarter_circle_roundtrips() {
let original = Arc {
center: Point3D {
x: 0.0,
y: 0.0,
z: 0.0,
},
radius: 10.0,
thickness: 0.0,
extrusion: default_extrusion(),
start_angle: 0.0,
end_angle: std::f64::consts::FRAC_PI_2,
};
let mut w = BitWriter::new();
original.encode(&mut w, Version::R2018).unwrap();
let bytes = w.into_bytes();
let mut c = BitCursor::new(&bytes);
let round = arc::decode(&mut c).unwrap();
assert_eq!(round.center, original.center);
assert_eq!(round.radius, original.radius);
assert_eq!(round.start_angle, original.start_angle);
assert!((round.end_angle - original.end_angle).abs() < 1e-12);
}

// -------- POINT round-trip --------

#[test]
fn point_default_fields_roundtrip() {
let original = Point {
position: Point3D {
x: 1.25,
y: 2.5,
z: 3.75,
},
thickness: 0.0,
extrusion: default_extrusion(),
x_axis_angle: 0.0,
};
let mut w = BitWriter::new();
original.encode(&mut w, Version::R2018).unwrap();
let bytes = w.into_bytes();
let mut c = BitCursor::new(&bytes);
let round = point::decode(&mut c).unwrap();
assert_eq!(round, original);
}

// -------- BE/BT encoder/decoder symmetry --------

#[test]
fn be_default_encodes_as_one_bit() {
let mut w = BitWriter::new();
write_be(&mut w, default_extrusion());
// 1 bit + 7 padding = 1 byte.
assert_eq!(w.as_slice().len(), 1);
// The one bit is true (0x80 after MSB-first packing).
assert_eq!(w.as_slice()[0] & 0x80, 0x80);
}

#[test]
fn bt_nonzero_encodes_flag_plus_bd() {
let mut w = BitWriter::new();
write_bt(&mut w, 2.5);
let bytes = w.into_bytes();
let mut c = BitCursor::new(&bytes);
// Flag bit should be false, then a BD follows.
let flag = c.read_b().unwrap();
assert!(!flag);
let read = c.read_bd().unwrap();
assert_eq!(read, 2.5);
}
}
Loading
Loading