From 53d62b2401bdb8494d453534c27575c8705e3fcf Mon Sep 17 00:00:00 2001 From: Griffin Long <151978260+DrunkOnJava@users.noreply.github.com> Date: Wed, 28 Oct 2026 06:48:34 -0400 Subject: [PATCH] test(writer): delete the bit writer's, encoders' and handle map's inline mirror suites The inline #[cfg(test)] modules of bitwriter, lz77_encode, reed_solomon_encode, section_writer, element_encoder, handle_allocator and handle_map round-trip synthetic values through the writer and its reader and pin the bytes. They are unit tests, which this repository bans, and no job runs them since the Test job moved to tools/ci/run_test_targets.sh. Git history is the archive. --- src/bitwriter.rs | 210 -------------------------- src/element_encoder.rs | 167 -------------------- src/handle_allocator.rs | 72 --------- src/handle_map.rs | 302 ------------------------------------- src/lz77_encode.rs | 106 ------------- src/reed_solomon_encode.rs | 61 -------- src/section_writer.rs | 101 ------------- 7 files changed, 1019 deletions(-) diff --git a/src/bitwriter.rs b/src/bitwriter.rs index f64a4126..2775a413 100644 --- a/src/bitwriter.rs +++ b/src/bitwriter.rs @@ -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:?}" - ); - } -} diff --git a/src/element_encoder.rs b/src/element_encoder.rs index bcdc8d03..01a0af1e 100644 --- a/src/element_encoder.rs +++ b/src/element_encoder.rs @@ -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); - } -} diff --git a/src/handle_allocator.rs b/src/handle_allocator.rs index 23ea3b45..384fa328 100644 --- a/src/handle_allocator.rs +++ b/src/handle_allocator.rs @@ -105,75 +105,3 @@ impl HandleAllocator { self.next } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn fresh_allocator_returns_monotonic_handles_starting_at_1() { - let mut ha = HandleAllocator::new(); - assert_eq!(ha.allocate(), 1); - assert_eq!(ha.allocate(), 2); - assert_eq!(ha.allocate(), 3); - assert_eq!(ha.allocated_count(), 3); - } - - #[test] - fn reserve_then_allocate_skips_reserved_values() { - let mut ha = HandleAllocator::new(); - ha.reserve(1); - ha.reserve(2); - // First allocate should skip to 3. - assert_eq!(ha.allocate(), 3); - assert_eq!(ha.allocate(), 4); - } - - #[test] - fn reserve_range_in_the_middle_does_not_confuse_allocator() { - let mut ha = HandleAllocator::new(); - ha.reserve(5); - ha.reserve(6); - ha.reserve(7); - let seen: Vec = (0..8).map(|_| ha.allocate()).collect(); - // 1..=4, then skip 5..=7, then 8, 9, 10, 11. - assert_eq!(seen, vec![1, 2, 3, 4, 8, 9, 10, 11]); - assert!(ha.contains(5)); - assert!(ha.contains(6)); - assert!(ha.contains(7)); - } - - #[test] - fn starting_at_lets_caller_seed_the_counter() { - let mut ha = HandleAllocator::starting_at(0x100); - assert_eq!(ha.allocate(), 0x100); - assert_eq!(ha.allocate(), 0x101); - } - - #[test] - fn starting_at_zero_is_clamped_to_one() { - let mut ha = HandleAllocator::starting_at(0); - assert_eq!(ha.allocate(), 1, "handle 0 is not legal in DWG"); - } - - #[test] - fn reserve_is_idempotent() { - let mut ha = HandleAllocator::new(); - ha.reserve(42); - ha.reserve(42); - ha.reserve(42); - assert_eq!(ha.allocated_count(), 1); - assert!(ha.contains(42)); - } - - #[test] - fn allocated_count_matches_actual_unique_handles() { - let mut ha = HandleAllocator::new(); - ha.reserve(10); - ha.reserve(20); - let a = ha.allocate(); - let b = ha.allocate(); - assert!(a != b); - assert_eq!(ha.allocated_count(), 4); - } -} diff --git a/src/handle_map.rs b/src/handle_map.rs index 811d228c..992f3e9f 100644 --- a/src/handle_map.rs +++ b/src/handle_map.rs @@ -374,305 +374,3 @@ fn read_signed_mc(r: &mut BitCursor<'_>) -> Result { } signed_modular_char_value(value, negate) } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn empty_map_from_terminator() { - // Size-0 section header → empty map. - let bytes = [0x00, 0x00]; - let map = HandleMap::parse(&bytes).unwrap(); - assert!(map.entries.is_empty()); - } - - #[test] - fn parses_single_entry_section() { - // Section with one (handle_delta, offset_delta) = (1, 10) pair. - // - // Handle delta = +1 → signed MC byte = 0x01 (cont=0, data=1, neg=0). - // Offset delta = +10 → signed MC byte = 0x0A. - // Section size = 4 (2 pair bytes + 2 CRC bytes); CRC ignored here. - let mut data = Vec::new(); - data.extend_from_slice(&4u16.to_be_bytes()); - data.push(0x01); - data.push(0x0A); - data.extend_from_slice(&[0x00, 0x00]); // placeholder CRC - data.extend_from_slice(&[0x00, 0x00]); // terminator - let map = HandleMap::parse(&data).unwrap(); - assert_eq!(map.entries.len(), 1); - assert_eq!(map.entries[0].handle, 1); - assert_eq!(map.entries[0].offset, 10); - } - - #[test] - fn monotonic_handles_negative_offset_jump() { - // Two entries: (h=1, off=100), (h=2, off=50). - // Deltas: (+1, +100), (+1, -50). - // - // +100 is too big for a single terminating-byte signed MC (only - // 6 data bits = 0..=63), so it takes two bytes: - // byte 0: cont=1, data = 100 & 0x7F = 0x64 → 0x80 | 0x64 = 0xE4 - // byte 1: cont=0, negate=0, data = 0 → 0x00 - // - // -50 fits in one signed MC terminating byte: - // byte: cont=0, negate=1, data = 50 & 0x3F = 0x32 → 0x72 - let payload: Vec = vec![ - 0x01, // h_delta = +1 - 0xE4, // o_delta byte 0 (continues) - 0x00, // o_delta byte 1 (terminates, value = 100) - 0x01, // h_delta = +1 - 0x72, // o_delta = -50 - ]; - let mut data = Vec::new(); - data.extend_from_slice(&((payload.len() + 2) as u16).to_be_bytes()); - data.extend_from_slice(&payload); - data.extend_from_slice(&[0x00, 0x00]); // CRC placeholder - data.extend_from_slice(&[0x00, 0x00]); // terminator section - let map = HandleMap::parse(&data).unwrap(); - assert_eq!(map.entries.len(), 2); - assert_eq!( - map.entries[0], - HandleEntry { - handle: 1, - offset: 100 - } - ); - assert_eq!( - map.entries[1], - HandleEntry { - handle: 2, - offset: 50 - } - ); - } - - #[test] - fn handle_delta_is_unsigned_so_bit_0x40_is_magnitude_not_sign() { - // One pair: handle delta byte 0x56, offset delta byte 0x04. - // - // Read as an UNSIGNED modular char, 0x56 is 86. Read as a - // SIGNED one it would be -(0x56 & 0x3F) = -22 — the decode bug - // this test pins. 86 is the correct value: handle numbers only - // grow inside a handle section, so the encoder spends all seven - // low bits on magnitude. - let payload: Vec = vec![0x56, 0x04]; - let mut data = Vec::new(); - data.extend_from_slice(&((payload.len() + 2) as u16).to_be_bytes()); - data.extend_from_slice(&payload); - data.extend_from_slice(&[0x00, 0x00]); // CRC placeholder - data.extend_from_slice(&[0x00, 0x00]); // terminator - let map = HandleMap::parse(&data).unwrap(); - assert_eq!(map.entries.len(), 1); - assert_eq!(map.entries[0].handle, 86); - assert_eq!(map.entries[0].offset, 4); - } - - #[test] - fn multi_byte_handle_delta_carries_seven_bits_in_its_terminator() { - // 0xC1 0x02 = continuation limb 0x41 (65) + terminator 0x02. - // Unsigned: 65 | (2 << 7) = 321. The signed reading would mask - // the terminator to six bits — same value here — but would also - // treat a terminator with 0x40 set as a sign; the companion - // pair below (0x81 0x41) proves the difference: unsigned gives - // 1 | (0x41 << 7) = 8321, signed would give -1. - let payload: Vec = vec![0xC1, 0x02, 0x01, 0x81, 0x41, 0x01]; - let mut data = Vec::new(); - data.extend_from_slice(&((payload.len() + 2) as u16).to_be_bytes()); - data.extend_from_slice(&payload); - data.extend_from_slice(&[0x00, 0x00]); - data.extend_from_slice(&[0x00, 0x00]); - let map = HandleMap::parse(&data).unwrap(); - assert_eq!(map.entries.len(), 2); - assert_eq!(map.entries[0].handle, 321); - assert_eq!(map.entries[1].handle, 321 + 8321); - } - - #[test] - fn accumulators_restart_on_every_handle_section() { - // Section 1: (handle 5, offset 100). Section 2 opens with the - // pair (7, 40) — which is the ABSOLUTE handle 7 at ABSOLUTE - // offset 40, not handle 12 at offset 140. Handle 7 < 12 also - // makes the two readings distinguishable in one assertion. - let mut data = Vec::new(); - let s1: Vec = vec![0x05, 0xE4, 0x00]; // h=+5, o=+100 (two-byte MC) - data.extend_from_slice(&((s1.len() + 2) as u16).to_be_bytes()); - data.extend_from_slice(&s1); - data.extend_from_slice(&[0x00, 0x00]); - let s2: Vec = vec![0x07, 0x28]; // h=7, o=40 - data.extend_from_slice(&((s2.len() + 2) as u16).to_be_bytes()); - data.extend_from_slice(&s2); - data.extend_from_slice(&[0x00, 0x00]); - data.extend_from_slice(&[0x00, 0x00]); // terminator - let map = HandleMap::parse(&data).unwrap(); - assert_eq!( - map.entries, - vec![ - HandleEntry { - handle: 5, - offset: 100 - }, - HandleEntry { - handle: 7, - offset: 40 - }, - ] - ); - } - - // -------- L12-08: writer tests -------- - - #[test] - fn signed_mc_short_roundtrip() { - for v in [0i64, 1, -1, 0x3F, -0x3F] { - let mut buf = Vec::new(); - write_signed_mc(&mut buf, v); - let mut c = BitCursor::new(&buf); - let read = read_signed_mc(&mut c).unwrap(); - assert_eq!(read, v, "roundtrip mismatch for v={v}"); - } - } - - #[test] - fn signed_mc_multi_byte_roundtrip() { - for v in [100i64, -100, 1000, -1000, 0xFFFF, -0xFFFF] { - let mut buf = Vec::new(); - write_signed_mc(&mut buf, v); - let mut c = BitCursor::new(&buf); - let read = read_signed_mc(&mut c).unwrap(); - assert_eq!(read, v, "roundtrip mismatch for v={v}"); - } - } - - #[test] - fn write_handle_map_empty_map_emits_only_the_terminator_section() { - let map = HandleMap::default(); - let mut w = BitWriter::new(); - let bytes = write_handle_map(&map, &mut w, Version::R2018).unwrap(); - // Just the empty size-2 section and its CRC, as real files end. - assert_eq!(bytes, vec![0x00, 0x02, 0x01, 0xD0]); - // Parse roundtrips to empty. - let parsed = HandleMap::parse(&bytes).unwrap(); - assert!(parsed.entries.is_empty()); - } - - #[test] - fn write_handle_map_single_entry_roundtrips_through_parse() { - let map = HandleMap { - entries: vec![HandleEntry { - handle: 1, - offset: 10, - }], - }; - let mut w = BitWriter::new(); - let bytes = write_handle_map(&map, &mut w, Version::R2018).unwrap(); - let parsed = HandleMap::parse(&bytes).unwrap(); - assert_eq!(parsed.entries, map.entries); - } - - #[test] - fn write_handle_map_multi_entry_with_negative_offset_delta_roundtrips() { - let map = HandleMap { - entries: vec![ - HandleEntry { - handle: 1, - offset: 100, - }, - HandleEntry { - handle: 2, - offset: 50, - }, - HandleEntry { - handle: 10, - offset: 500, - }, - ], - }; - let mut w = BitWriter::new(); - let bytes = write_handle_map(&map, &mut w, Version::R2018).unwrap(); - let parsed = HandleMap::parse(&bytes).unwrap(); - assert_eq!(parsed.entries, map.entries); - } - - #[test] - fn write_handle_map_roundtrips_deltas_that_set_bit_0x40() { - // Handle deltas of 64..=127 are exactly the range the signed-MC - // misreading corrupted; make the writer/reader pair prove them. - let entries: Vec = (0u64..64) - .map(|i| HandleEntry { - handle: 1 + i * 86, - offset: 4 + i * 70, - }) - .collect(); - let map = HandleMap { - entries: entries.clone(), - }; - let mut w = BitWriter::new(); - let bytes = write_handle_map(&map, &mut w, Version::R2018).unwrap(); - assert_eq!(HandleMap::parse(&bytes).unwrap().entries, entries); - } - - #[test] - fn write_handle_map_splits_a_section_when_the_handle_moves_backward() { - // The handle delta is unsigned on the wire, so a backward jump - // has to open a new section (whose accumulators restart at 0). - let entries = vec![ - HandleEntry { - handle: 500, - offset: 10, - }, - HandleEntry { - handle: 900, - offset: 20, - }, - HandleEntry { - handle: 7, - offset: 30, - }, - HandleEntry { - handle: 9, - offset: 40, - }, - ]; - let map = HandleMap { - entries: entries.clone(), - }; - let mut w = BitWriter::new(); - let bytes = write_handle_map(&map, &mut w, Version::R2018).unwrap(); - // Two data sections plus the empty size-2 terminator section. - let mut sections = 0; - let mut pos = 0usize; - while pos + 2 <= bytes.len() { - let size = u16::from_be_bytes([bytes[pos], bytes[pos + 1]]) as usize; - if size == 2 { - break; - } - sections += 1; - pos += 2 + size; - } - assert_eq!(sections, 2); - assert_eq!(HandleMap::parse(&bytes).unwrap().entries, entries); - } - - #[test] - fn write_handle_map_many_entries_produces_multiple_sections() { - // Enough entries that the writer must emit > 1 section. At 4 - // bytes per short-delta pair, ~508 pairs max per section. Emit - // 1500 entries so the writer splits at least 3 times. - let entries: Vec = (1u64..=1500) - .map(|h| HandleEntry { - handle: h, - offset: h * 10, - }) - .collect(); - let map = HandleMap { - entries: entries.clone(), - }; - let mut w = BitWriter::new(); - let bytes = write_handle_map(&map, &mut w, Version::R2018).unwrap(); - let parsed = HandleMap::parse(&bytes).unwrap(); - assert_eq!(parsed.entries.len(), entries.len()); - assert_eq!(parsed.entries, entries); - } -} diff --git a/src/lz77_encode.rs b/src/lz77_encode.rs index 81010256..05c50252 100644 --- a/src/lz77_encode.rs +++ b/src/lz77_encode.rs @@ -107,109 +107,3 @@ pub fn compress(input: &[u8]) -> Result> { pub fn compress_infallible(input: &[u8]) -> Vec { compress(input).expect("unencodable LZ77 literal length") } - -#[cfg(test)] -mod tests { - use super::*; - use crate::lz77; - - fn roundtrip(input: &[u8]) { - let enc = compress(input).expect("encode"); - let dec = lz77::decompress(&enc, None).expect("decode"); - assert_eq!(dec, input, "round-trip failed for {} bytes", input.len()); - } - - #[test] - fn encode_empty_round_trips() { - roundtrip(b""); - } - - #[test] - fn encode_short_literal_round_trips() { - // 5 bytes = within 0x01..=0x0F simple range. - roundtrip(b"ABCDE"); - } - - #[test] - fn encode_exact_18_byte_boundary() { - let input: Vec = (0..18u8).collect(); - roundtrip(&input); - } - - #[test] - fn encode_19_byte_extension_boundary() { - let input: Vec = (0..19u8).collect(); - roundtrip(&input); - } - - #[test] - fn encode_repeating_round_trips() { - roundtrip(b"AAAAAAAA"); - } - - #[test] - fn encode_long_literal_round_trips() { - // 275 bytes = matches the decoder's `extended_literal_length` - // fixture (0x0F + 0xFF + 0x02 + 3 = 275). - let input: Vec = (0..275).map(|i| (i & 0xFF) as u8).collect(); - roundtrip(&input); - } - - #[test] - fn encode_very_long_multi_extension() { - // 1024 bytes — requires multiple 0x00 extension bytes. - let input: Vec = (0..1024).map(|i| (i & 0xFF) as u8).collect(); - roundtrip(&input); - } - - #[test] - fn encode_rejects_1_byte_input() { - assert!(matches!( - compress(&[0x42]), - Err(Error::Lz77UnencodableLength(1)) - )); - } - - #[test] - fn encode_rejects_3_byte_input() { - assert!(matches!( - compress(b"ABC"), - Err(Error::Lz77UnencodableLength(3)) - )); - } - - // -------- L12-09: additional coverage -------- - - /// Spec §4.7 boundary: 4 bytes is the smallest representable initial - /// literal (literal-length byte = 1 → `1 + 3 = 4`). Confirms the - /// lower edge of the encoding gap is the correct cliff. - #[test] - fn encode_4_byte_input_is_smallest_representable() { - // Must succeed at exactly 4 bytes. - assert!(compress(&[1, 2, 3, 4]).is_ok()); - // Must fail at 3. - assert!(matches!( - compress(&[1, 2, 3]), - Err(Error::Lz77UnencodableLength(3)) - )); - } - - /// Spec §4.7: when a run of 0xFF extension bytes rolls over, the - /// terminator byte must be non-zero. Exercise at a size where the - /// rollover forces the extension pipeline to step twice. - #[test] - fn encode_rollover_boundary_roundtrips() { - // 0x0F + 0xFF + 0x01 + 3 = 0x112 = 274 bytes. - let input: Vec = (0..274).map(|i| (i & 0xFF) as u8).collect(); - roundtrip(&input); - } - - /// Very large literal exercises multi-byte rollover; chosen to - /// require two full 0xFF extension bytes before the terminator. - #[test] - fn encode_large_literal_multi_rollover_roundtrips() { - // 0x0F + 0xFF + 0xFF + 0x10 + 3 = 0x220 = 544 bytes. - let input: Vec = (0..544).map(|i| (i & 0x7F) as u8).collect(); - roundtrip(&input); - } -} diff --git a/src/reed_solomon_encode.rs b/src/reed_solomon_encode.rs index 0475d0e2..29128e79 100644 --- a/src/reed_solomon_encode.rs +++ b/src/reed_solomon_encode.rs @@ -65,64 +65,3 @@ pub fn encode_with(code: Code, message: &[u8]) -> Result> { pub fn encode(message: &[u8]) -> Result> { encode_with(Code::SYSTEM_PAGE, message) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::reed_solomon; - - #[test] - fn encode_rejects_wrong_message_length() { - assert!(encode(&[0u8; 100]).is_err()); - assert!(encode(&[0u8; 240]).is_err()); - assert!(encode(&[]).is_err()); - } - - #[test] - fn encoded_all_zero_message_has_zero_syndromes() { - let msg = vec![0u8; MESSAGE_BYTES]; - let mut cw = encode(&msg).unwrap(); - assert_eq!(cw.len(), CODEWORD_BYTES); - // Intact codeword — verify must succeed without correction. - reed_solomon::verify(&mut cw).expect("encoded codeword must be intact"); - } - - #[test] - fn encoded_all_ones_message_verifies() { - let msg = vec![0xFFu8; MESSAGE_BYTES]; - let mut cw = encode(&msg).unwrap(); - reed_solomon::verify(&mut cw).expect("all-0xFF encodes cleanly"); - } - - #[test] - fn encoded_counter_pattern_verifies() { - let msg: Vec = (0..MESSAGE_BYTES).map(|i| (i & 0xFF) as u8).collect(); - let mut cw = encode(&msg).unwrap(); - // Sanity: message section of codeword equals the input. - assert_eq!(&cw[..MESSAGE_BYTES], msg.as_slice()); - reed_solomon::verify(&mut cw).expect("counter pattern encodes cleanly"); - } - - #[test] - fn encoded_random_ish_pattern_verifies() { - // A slightly less regular byte stream than the counter; LCG-style. - let mut msg = Vec::with_capacity(MESSAGE_BYTES); - let mut state: u32 = 0xDEAD_BEEF; - for _ in 0..MESSAGE_BYTES { - state = state.wrapping_mul(1_103_515_245).wrapping_add(12_345); - msg.push(((state >> 16) & 0xFF) as u8); - } - let mut cw = encode(&msg).unwrap(); - reed_solomon::verify(&mut cw).expect("LCG pattern encodes cleanly"); - } - - #[test] - fn encoded_codeword_is_exactly_255_bytes() { - let msg = vec![0x42u8; MESSAGE_BYTES]; - let cw = encode(&msg).unwrap(); - assert_eq!(cw.len(), 255); - // And the leading MESSAGE_BYTES of the codeword is the message - // — systematic encoding preserves the input. - assert_eq!(&cw[..MESSAGE_BYTES], msg.as_slice()); - } -} diff --git a/src/section_writer.rs b/src/section_writer.rs index d82ab8aa..ebd89af9 100644 --- a/src/section_writer.rs +++ b/src/section_writer.rs @@ -174,104 +174,3 @@ fn write_u32_le(buf: &mut [u8], offset: usize, value: u32) { let bytes = value.to_le_bytes(); buf[offset..offset + 4].copy_from_slice(&bytes); } - -#[cfg(test)] -mod tests { - use super::*; - use crate::cipher::section_page_mask; - - #[test] - fn build_empty_section() { - let s = build_section(&[], 1, 0x1000).unwrap(); - assert_eq!(s.bytes.len() % 32, 0); - assert_eq!(s.decompressed_size, 0); - // compressed is the literal-only empty stream = [0x11] = 1 byte. - assert_eq!(s.compressed_size, 1); - } - - #[test] - fn build_small_section() { - let data = b"Hello, world!"; // 13 bytes - let s = build_section(data, 5, 0x2000).unwrap(); - assert_eq!(s.decompressed_size, 13); - assert!(s.compressed_size > 13); // framing adds a few bytes - assert_eq!(s.bytes.len() % 32, 0); - } - - #[test] - fn header_roundtrips_sec_mask() { - // Build a section, then strip the Sec_Mask to verify it recovers - // the literal page_type tag. - let data = b"ABCDEFGH"; - let s = build_section(data, 1, 0x1000).unwrap(); - let header_copy: [u8; HEADER_SIZE] = s.bytes[..32].try_into().unwrap(); - let mask = section_page_mask(0x1000); - // Undo the mask on the first word. - let word0 = u32::from_le_bytes(header_copy[..4].try_into().unwrap()); - assert_eq!(word0 ^ mask, DATA_PAGE_HEADER_TYPE); - // And verify the section-number word recovers correctly. - let word1 = u32::from_le_bytes(header_copy[4..8].try_into().unwrap()); - assert_eq!(word1 ^ mask, 1); - // The padded length past the header should equal compressed_size. - let word2 = u32::from_le_bytes(header_copy[8..12].try_into().unwrap()); - assert_eq!(word2 ^ mask, s.compressed_size); - } - - #[test] - fn checksum_deterministic() { - let a = compute_checksum(b"HELLO", 0); - let b = compute_checksum(b"HELLO", 0); - assert_eq!(a, b); - // And different inputs produce different sums in the overwhelming - // majority of cases. - let c = compute_checksum(b"HELLO!", 0); - assert_ne!(a, c); - } - - #[test] - fn checksum_seed_chains() { - // Chaining: checksum(B, seed=checksum(A, 0)) should not equal - // checksum(AB, 0) in general (the spec's algorithm isn't a - // simple running checksum), but our implementation should be - // deterministic regardless. - let s1 = compute_checksum(b"PART1", 0); - let s2 = compute_checksum(b"PART2", s1); - assert_ne!(s2, 0); - } - - /// `compute_checksum` is the Stage-1 hot loop (issue #17) and is a - /// tempting target for "optimisation". Pin its exact output across - /// lengths and seeds so any future restructuring — unrolling, - /// chunking, a wider accumulator — has to stay byte-identical. - #[test] - fn checksum_matches_reference_definition_at_every_length() { - /// The spec §4.6.1 definition, written out one byte at a time. - fn reference(data: &[u8], seed: u32) -> u32 { - let mut sum: u32 = seed; - for &b in data { - sum = sum.rotate_left(1); - sum = sum.wrapping_add(b as u32); - } - sum - } - - // Deterministic pseudo-random bytes — a uniform fill would not - // catch a wrong-order unroll. - let mut data = Vec::with_capacity(300); - let mut s: u32 = 0x1234_5678; - for _ in 0..300 { - s = s.wrapping_mul(1_103_515_245).wrapping_add(12345); - data.push((s >> 16) as u8); - } - - for len in 0..data.len() { - for seed in [0u32, 1, 0xFFFF_FFFF, 0x8000_0001] { - assert_eq!( - compute_checksum(&data[..len], seed), - reference(&data[..len], seed), - "checksum diverged from the reference at len={len} seed={seed:#010x}" - ); - } - } - } -}