From aa606b16f8a0b698b18e64bff585f7ac778f27c0 Mon Sep 17 00:00:00 2001 From: Griffin Long <151978260+DrunkOnJava@users.noreply.github.com> Date: Wed, 28 Oct 2026 06:49:26 -0400 Subject: [PATCH] test(container): delete the container, section and primitive inline mirror suites The inline #[cfg(test)] modules of bitcursor, cipher, crc, lz77, r2007, r21_lz, reed_solomon, section, section_map, header, metadata, limits, api and object_type build synthetic byte buffers in each parser's own layout and assert its fields. 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/api.rs | 123 --------------------- src/bitcursor.rs | 261 -------------------------------------------- src/cipher.rs | 51 --------- src/crc.rs | 122 --------------------- src/header.rs | 23 ---- src/limits.rs | 97 ---------------- src/lz77.rs | 235 --------------------------------------- src/metadata.rs | 47 -------- src/object_type.rs | 50 --------- src/r2007.rs | 129 ---------------------- src/r21_lz.rs | 106 ------------------ src/reed_solomon.rs | 38 ------- src/section.rs | 125 --------------------- src/section_map.rs | 60 ---------- 14 files changed, 1467 deletions(-) diff --git a/src/api.rs b/src/api.rs index 544a4a05..4a80a3a0 100644 --- a/src/api.rs +++ b/src/api.rs @@ -379,126 +379,3 @@ pub fn assert_no_unknown_objects(file: &DwgFile, version: Version) -> Result<()> } Ok(()) } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn parsemode_default_is_best_effort() { - assert_eq!(ParseMode::default(), ParseMode::BestEffort); - } - - #[test] - fn decoded_complete_has_no_warnings() { - let d = Decoded::complete(42); - assert!(d.complete); - assert!(d.diagnostics.warnings.is_empty()); - } - - #[test] - fn decoded_partial_preserves_diagnostics() { - let mut diag = Diagnostics::default(); - diag.warn("test", "something went off"); - let d = Decoded::partial("value", diag); - assert!(!d.complete); - assert_eq!(d.diagnostics.warnings.len(), 1); - } - - #[test] - fn decoded_map_preserves_diagnostics() { - let mut diag = Diagnostics::default(); - diag.warn("a", "b"); - let d = Decoded::partial(10_i32, diag); - let mapped = d.map(|n| n * 2); - assert_eq!(mapped.value, 20); - assert_eq!(mapped.diagnostics.warnings.len(), 1); - assert!(!mapped.complete); - } - - #[test] - fn diagnostics_is_clean_on_default() { - let d = Diagnostics::default(); - assert!(d.is_clean()); - } - - #[test] - fn diagnostics_warn_mutates() { - let mut d = Diagnostics::default(); - d.warn("x", "y"); - assert!(!d.is_clean()); - assert_eq!(d.warnings[0].code, "x"); - assert_eq!(d.warnings[0].message, "y"); - assert_eq!(d.warnings[0].bit_offset, None); - } - - #[test] - fn diagnostics_warn_at_carries_offset() { - let mut d = Diagnostics::default(); - d.warn_at("truncated", 1234, "section ran short"); - assert_eq!(d.warnings[0].bit_offset, Some(1234)); - } - - #[test] - fn confidence_zero_attempts_is_one() { - let d = Diagnostics::default(); - assert_eq!(d.confidence(0), 1.0); - } - - #[test] - fn confidence_all_clean_is_one() { - let d = Diagnostics::default(); - assert_eq!(d.confidence(100), 1.0); - } - - #[test] - fn confidence_counts_all_failure_modes() { - let mut d = Diagnostics { - skipped_records: 10, - failed_streams: 5, - partial_fields: 3, - ..Default::default() - }; - d.warn("a", "b"); - d.warn("c", "d"); - // 20 bad out of 100 → confidence 0.8 - assert_eq!(d.confidence(100), 0.8); - } - - #[test] - fn decoded_complete_initializes_new_fields() { - let d = Decoded::complete(7_u8); - assert_eq!(d.consumed_bits, 0); - assert_eq!(d.stopped_at_field, None); - } - - #[test] - fn decoded_partial_initializes_new_fields() { - let d = Decoded::partial(7_u8, Diagnostics::default()); - assert_eq!(d.consumed_bits, 0); - assert_eq!(d.stopped_at_field, None); - } - - #[test] - fn decoded_partial_at_captures_marker() { - let mut diag = Diagnostics::default(); - diag.warn("truncated_field", "ran out of bits in flags"); - let d = Decoded::partial_at(0_u32, diag, 1_234, "flags"); - assert!(!d.complete); - assert_eq!(d.consumed_bits, 1_234); - assert_eq!(d.stopped_at_field, Some("flags")); - assert_eq!(d.diagnostics.warnings[0].code, "truncated_field"); - } - - #[test] - fn decoded_map_preserves_partial_at_markers() { - let mut diag = Diagnostics::default(); - diag.warn("x", "y"); - let d = Decoded::partial_at(10_i32, diag, 64, "thickness"); - let mapped = d.map(|n| n.to_string()); - assert_eq!(mapped.value, "10"); - assert_eq!(mapped.consumed_bits, 64); - assert_eq!(mapped.stopped_at_field, Some("thickness")); - assert!(!mapped.complete); - } -} diff --git a/src/bitcursor.rs b/src/bitcursor.rs index 8bc0e903..1f98fd85 100644 --- a/src/bitcursor.rs +++ b/src/bitcursor.rs @@ -479,264 +479,3 @@ impl Handle { matches!(self.code, 0x6 | 0x8 | 0xA | 0xC) } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::bitwriter::BitWriter; - - #[test] - fn read_handle_accepts_a_full_eight_byte_counter() { - let mut w = BitWriter::new(); - w.write_handle(4, u64::MAX); - let bytes = w.into_bytes(); - let h = BitCursor::new(&bytes).read_handle().unwrap(); - assert_eq!(h.code, 4); - assert_eq!(h.counter, 8); - assert_eq!(h.value, u64::MAX); - } - - #[test] - fn read_handle_rejects_a_counter_above_eight() { - // Code 2, counter 11 — the signature the sample_AC1032.dwg - // phantom records carried (#44/#51). Eleven value bytes cannot - // fit a u64, so the cursor is provably not on a handle field. - let mut w = BitWriter::new(); - w.write_rc(0x2B); - for b in 0..11u8 { - w.write_rc(b); - } - let bytes = w.into_bytes(); - let err = BitCursor::new(&bytes).read_handle().unwrap_err(); - match err { - Error::HandleCounterTooLarge { code, counter } => { - assert_eq!(code, 2); - assert_eq!(counter, 11); - } - other => panic!("expected HandleCounterTooLarge, got {other:?}"), - } - } - - /// Helper: pack a sequence of `(value, bit_count)` pairs MSB-first into a - /// byte vector. Pads the final byte with zeros. Used to construct bit - /// streams for spec example tests without pen-and-paper arithmetic. - fn pack_bits(fields: &[(u64, u32)]) -> Vec { - let mut acc: u128 = 0; - let mut nbits: u32 = 0; - for &(v, k) in fields { - assert!(k <= 64); - assert!(nbits + k <= 128, "test helper overflow"); - acc = (acc << k) | (v as u128); - nbits += k; - } - // pad to byte boundary - let pad = (8 - (nbits % 8)) % 8; - acc <<= pad as u128; - let total = nbits + pad; - let mut out = Vec::with_capacity((total / 8) as usize); - let mut remaining = total; - while remaining > 0 { - remaining -= 8; - out.push(((acc >> remaining) & 0xFF) as u8); - } - out - } - - // Spec §2.2 — BITSHORT worked example: - // Stream: 00 00000001 00000001 10 11 01 00001111 10 - // 5 shorts: 257, 0, 256, 15, 0 - #[test] - fn spec_2_2_bitshort_example() { - let stream = pack_bits(&[ - (0b00, 2), // tag 00 → 16-bit LE - (0b0000_0001, 8), // LSB - (0b0000_0001, 8), // MSB → 257 - (0b10, 2), // → 0 - (0b11, 2), // → 256 - (0b01, 2), // tag 01 → 8-bit - (0b0000_1111, 8), // = 15 - (0b10, 2), // → 0 - ]); - let mut c = BitCursor::new(&stream); - assert_eq!(c.read_bs().unwrap(), 257); - assert_eq!(c.read_bs().unwrap(), 0); - assert_eq!(c.read_bs().unwrap(), 256); - assert_eq!(c.read_bs().unwrap(), 15); - assert_eq!(c.read_bs().unwrap(), 0); - } - - // Spec §2.3 — BITLONG worked example: - // Stream: 00 00000001 00000001 00000000 00000000 10 01 00001111 10 - // 4 longs: 257, 0, 15, 0 - #[test] - fn spec_2_3_bitlong_example() { - let stream = pack_bits(&[ - (0b00, 2), // tag 00 → 32-bit LE - (0x01, 8), // byte 0 (LSB) - (0x01, 8), // byte 1 - (0x00, 8), // byte 2 - (0x00, 8), // byte 3 (MSB) → 257 - (0b10, 2), // → 0 - (0b01, 2), // tag 01 → 8-bit - (0b0000_1111, 8), // = 15 - (0b10, 2), // → 0 - ]); - let mut c = BitCursor::new(&stream); - assert_eq!(c.read_bl().unwrap(), 257); - assert_eq!(c.read_bl().unwrap(), 0); - assert_eq!(c.read_bl().unwrap(), 15); - assert_eq!(c.read_bl().unwrap(), 0); - } - - // Spec §2.5 — BITDOUBLE special cases + one full double. - #[test] - fn spec_2_5_bitdouble_specials() { - // Prefix 01 10 00 then 8 LE bytes of 2.0. - let bytes_2p0 = 2.0f64.to_le_bytes(); - let mut fields: Vec<(u64, u32)> = vec![ - (0b01, 2), // → 1.0 - (0b10, 2), // → 0.0 - (0b00, 2), // → read 8 raw LE bytes - ]; - for b in bytes_2p0 { - fields.push((b as u64, 8)); - } - let stream = pack_bits(&fields); - let mut c = BitCursor::new(&stream); - assert_eq!(c.read_bd().unwrap(), 1.0); - assert_eq!(c.read_bd().unwrap(), 0.0); - assert_eq!(c.read_bd().unwrap(), 2.0); - } - - // Spec §2.5 — DD (bitdouble with default) examples. - #[test] - fn spec_2_5_bitdouble_with_default_variants() { - let default = 12.5f64; - let patch4 = f64::from_le_bytes([1, 2, 3, 4, 0, 0, 0x29, 0x40]); - let patch6 = f64::from_le_bytes([9, 8, 7, 6, 5, 4, 0x29, 0x40]); - let full = -42.25f64; - - let fields: Vec<(u64, u32)> = vec![ - (0b00, 2), - (0b01, 2), - (1, 8), - (2, 8), - (3, 8), - (4, 8), - (0b10, 2), - (5, 8), - (4, 8), - (9, 8), - (8, 8), - (7, 8), - (6, 8), - ]; - - let stream = pack_bits(&fields); - let mut c = BitCursor::new(&stream); - assert_eq!(c.read_dd(default).unwrap(), default); - assert_eq!(c.read_dd(default).unwrap(), patch4); - assert_eq!(c.read_dd(default).unwrap(), patch6); - - let mut full_fields: Vec<(u64, u32)> = vec![(0b11, 2)]; - for b in full.to_le_bytes() { - full_fields.push((b as u64, 8)); - } - let stream = pack_bits(&full_fields); - let mut c = BitCursor::new(&stream); - assert_eq!(c.read_dd(default).unwrap(), full); - } - - // §2.0 — single bits and 2-bit codes - #[test] - fn single_bits() { - let bytes = [0b1010_0000]; - let mut c = BitCursor::new(&bytes); - assert!(c.read_b().unwrap()); - assert!(!c.read_b().unwrap()); - assert!(c.read_b().unwrap()); - assert!(!c.read_b().unwrap()); - } - - #[test] - fn two_bit_codes() { - // 11 01 10 00 → 3, 1, 2, 0 - let bytes = [0b1101_1000]; - let mut c = BitCursor::new(&bytes); - assert_eq!(c.read_bb().unwrap(), 3); - assert_eq!(c.read_bb().unwrap(), 1); - assert_eq!(c.read_bb().unwrap(), 2); - assert_eq!(c.read_bb().unwrap(), 0); - } - - // §2.1 — 3B - #[test] - fn read_3b_is_three_raw_bits() { - for value in 0u8..=7 { - let bytes = [value << 5]; - assert_eq!( - BitCursor::new(&bytes).read_3b().unwrap(), - value, - "value={value}" - ); - } - // Three bits, always — the next read starts at bit 3. - let bytes = [0b0011_1111]; - let mut c = BitCursor::new(&bytes); - assert_eq!(c.read_3b().unwrap(), 0b001); - assert_eq!(c.position_bits(), 3); - } - - // Byte alignment between objects, per §2.14. - #[test] - fn align_to_byte_drops_partial() { - let bytes = [0b1010_0000, 0xAB]; - let mut c = BitCursor::new(&bytes); - // Consume 3 bits; cursor at bit 3. - let _ = c.read_bits(3).unwrap(); - c.align_to_byte(); - assert_eq!(c.read_rc().unwrap(), 0xAB); - } - - #[test] - fn remaining_bits_tracks_position() { - let bytes = [0xFF, 0xFF]; - let mut c = BitCursor::new(&bytes); - assert_eq!(c.remaining_bits(), 16); - let _ = c.read_bits(5).unwrap(); - assert_eq!(c.remaining_bits(), 11); - } - - #[test] - fn exhaustion_returns_error() { - let bytes = [0xFF]; - let mut c = BitCursor::new(&bytes); - let _ = c.read_bits(8).unwrap(); - assert!(matches!(c.read_b(), Err(Error::BitsExhausted { .. }))); - } - - // §2.13 — Handle reference - // - // Spec example: LAYER 0 has handle 0x0F stored as 5.1.0F - // → CODE=5 COUNTER=1 HANDLE=0x0F - // → bitstream: 0101 0001 00001111 - #[test] - fn handle_layer_0_example() { - let bytes = [0b0101_0001, 0b0000_1111]; - 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); - assert!(h.is_absolute()); - assert!(!h.is_offset()); - } - - #[test] - fn raw_types_are_byte_aligned_le() { - let bytes = [0x01, 0x00, 0x02, 0x00, 0x00, 0x00]; - let mut c = BitCursor::new(&bytes); - assert_eq!(c.read_rs().unwrap(), 1); - assert_eq!(c.read_rl().unwrap(), 2); - } -} diff --git a/src/cipher.rs b/src/cipher.rs index 0ac301bb..493665ce 100644 --- a/src/cipher.rs +++ b/src/cipher.rs @@ -82,54 +82,3 @@ pub fn xor_in_place(block: &mut [u8]) { pub fn section_page_mask(offset: u32) -> u32 { 0x4164_536B ^ offset } - -#[cfg(test)] -mod tests { - use super::*; - - /// The first 16 bytes of the sequence are published on page 24 of - /// spec v5.4.1. Match them exactly. - #[test] - fn matches_spec_published_prefix() { - let m = magic_sequence(); - let expected: [u8; 16] = [ - 0x29, 0x23, 0xBE, 0x84, 0xE1, 0x6C, 0xD6, 0xAE, 0x52, 0x90, 0x49, 0xF1, 0xF1, 0xBB, - 0xE9, 0xEB, - ]; - assert_eq!(&m[..16], &expected); - } - - /// The full 0x6C-byte sequence from the spec. - #[test] - fn matches_spec_full_sequence() { - let m = magic_sequence(); - let expected: [u8; 108] = [ - 0x29, 0x23, 0xBE, 0x84, 0xE1, 0x6C, 0xD6, 0xAE, 0x52, 0x90, 0x49, 0xF1, 0xF1, 0xBB, - 0xE9, 0xEB, 0xB3, 0xA6, 0xDB, 0x3C, 0x87, 0x0C, 0x3E, 0x99, 0x24, 0x5E, 0x0D, 0x1C, - 0x06, 0xB7, 0x47, 0xDE, 0xB3, 0x12, 0x4D, 0xC8, 0x43, 0xBB, 0x8B, 0xA6, 0x1F, 0x03, - 0x5A, 0x7D, 0x09, 0x38, 0x25, 0x1F, 0x5D, 0xD4, 0xCB, 0xFC, 0x96, 0xF5, 0x45, 0x3B, - 0x13, 0x0D, 0x89, 0x0A, 0x1C, 0xDB, 0xAE, 0x32, 0x20, 0x9A, 0x50, 0xEE, 0x40, 0x78, - 0x36, 0xFD, 0x12, 0x49, 0x32, 0xF6, 0x9E, 0x7D, 0x49, 0xDC, 0xAD, 0x4F, 0x14, 0xF2, - 0x44, 0x40, 0x66, 0xD0, 0x6B, 0xC4, 0x30, 0xB7, 0x32, 0x3B, 0xA1, 0x22, 0xF6, 0x22, - 0x91, 0x9D, 0xE1, 0x8B, 0x1F, 0xDA, 0xB0, 0xCA, 0x99, 0x02, - ]; - assert_eq!(m, expected); - } - - /// XOR is its own inverse: encrypting then decrypting restores input. - #[test] - fn xor_is_involution() { - let original: Vec = (0u8..(MAGIC_LEN as u8)).collect(); - let mut buf = original.clone(); - xor_in_place(&mut buf); - assert_ne!(buf, original); - xor_in_place(&mut buf); - assert_eq!(buf, original); - } - - #[test] - fn section_page_mask_changes_with_offset() { - assert_ne!(section_page_mask(0x100), section_page_mask(0x200)); - assert_eq!(section_page_mask(0) ^ 0x4164_536B, 0); - } -} diff --git a/src/crc.rs b/src/crc.rs index cf71ed62..62b75eb7 100644 --- a/src/crc.rs +++ b/src/crc.rs @@ -304,125 +304,3 @@ pub fn page_checksums(header_zeroed: &[u8], payload: &[u8]) -> (u32, u32) { let payload_cs = section_page_checksum(header_cs, payload); (header_cs, payload_cs) } - -#[cfg(test)] -mod tests { - use super::*; - - /// A CRC table has a trivially-testable invariant: index 0 must be 0 - /// (the CRC of an empty byte at state 0 stays 0). - #[test] - fn crc8_table_index_0_is_zero() { - assert_eq!(CRC8_TABLE[0], 0x0000); - } - - /// Self-consistency: running the table through a known byte sequence - /// and comparing against a direct polynomial evaluation of the same - /// reflected CRC-16 family (poly 0xA001 = reflect of 0x8005). - #[test] - fn crc8_matches_reflected_poly_a001() { - // Bitwise-compute the CRC for "123456789" (classic CRC test vector). - let msg = b"123456789"; - let mut bit_crc: u16 = 0; - for &b in msg { - bit_crc ^= b as u16; - for _ in 0..8 { - if bit_crc & 1 != 0 { - bit_crc = (bit_crc >> 1) ^ 0xA001; - } else { - bit_crc >>= 1; - } - } - } - let tbl_crc = crc8(0, msg); - assert_eq!(tbl_crc, bit_crc); - } - - #[test] - fn crc32_ieee_test_vector() { - // "123456789" under IEEE 802.3 CRC-32 is 0xCBF43926. - assert_eq!(crc32(0, b"123456789"), 0xCBF4_3926); - } - - #[test] - fn r13_locator_seeds_known_table() { - assert_eq!(r13_locator_seed(3), Some(0xA598)); - assert_eq!(r13_locator_seed(6), Some(0x8461)); - assert_eq!(r13_locator_seed(99), None); - } - - #[test] - fn section_page_checksum_empty_is_seed() { - assert_eq!(section_page_checksum(0, &[]), 0); - assert_eq!(section_page_checksum(0xDEAD_BEEF, &[]), 0xDEAD_BEEF); - } - - #[test] - fn section_page_checksum_is_deterministic() { - let data = b"The DWG file format version 2004 compression is a variation on LZ77."; - let a = section_page_checksum(0, data); - let b = section_page_checksum(0, data); - assert_eq!(a, b); - // Non-zero for non-empty input. - assert_ne!(a, 0); - } - - // -------- L12-02: writer helpers -------- - - #[test] - fn embed_crc8_round_trips() { - // Build a section with a 2-byte CRC slot at offset 4, then - // verify that reading the slot + recomputing the CRC over the - // zeroed payload reproduces the embedded value. - let mut buf = vec![0xAA, 0xBB, 0xCC, 0xDD, 0xFF, 0xFF, 0x01, 0x02, 0x03]; - let written = embed_crc8(&mut buf, 4, 0).expect("slot fits"); - // Read back the embedded LE pair. - let embedded = u16::from_le_bytes([buf[4], buf[5]]); - assert_eq!(embedded, written); - // Reconstruct zero-filled buffer and re-verify the CRC. - let mut verify = buf.clone(); - verify[4] = 0; - verify[5] = 0; - assert_eq!(crc8(0, &verify), written); - } - - #[test] - fn embed_crc8_rejects_too_small_buffer() { - let mut buf = vec![0u8; 3]; - assert!(embed_crc8(&mut buf, 2, 0).is_none()); - // Exactly fits at offset 1. - let mut buf2 = vec![0u8; 3]; - assert!(embed_crc8(&mut buf2, 1, 0).is_some()); - } - - #[test] - fn embed_crc32_round_trips_with_zeroed_slot() { - let mut buf = vec![0x01, 0x02, 0x03, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0x05]; - let written = embed_crc32(&mut buf, 4, 0).expect("slot fits"); - let embedded = u32::from_le_bytes(buf[4..8].try_into().unwrap()); - assert_eq!(embedded, written); - let mut verify = buf.clone(); - verify[4..8].fill(0); - assert_eq!(crc32(0, &verify), written); - } - - #[test] - fn embed_crc32_rejects_too_small_buffer() { - let mut buf = vec![0u8; 5]; - assert!(embed_crc32(&mut buf, 3, 0).is_none()); // only 2 bytes fit past 3 - let mut buf2 = vec![0u8; 5]; - assert!(embed_crc32(&mut buf2, 1, 0).is_some()); // 4 bytes fit at [1..5] - } - - #[test] - fn page_checksums_chains_header_into_payload() { - // The payload checksum seeds from the header checksum, so - // changing the header changes the payload output even if - // payload bytes are identical. - let payload = b"identical payload"; - let (h1, p1) = page_checksums(&[0u8; 16], payload); - let (h2, p2) = page_checksums(&[1u8; 16], payload); - assert_ne!(h1, h2); - assert_ne!(p1, p2); - } -} diff --git a/src/header.rs b/src/header.rs index 4297230b..ce799b46 100644 --- a/src/header.rs +++ b/src/header.rs @@ -270,26 +270,3 @@ impl R2004Header { }) } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn common_header_truncated() { - let bytes = b"AC1015"; // just the magic, nothing else - assert!(matches!( - CommonHeader::parse(bytes), - Err(Error::Truncated { .. }) - )); - } - - #[test] - fn locator_record_round_trip() { - let raw = [0x01u8, 0x78, 0x56, 0x34, 0x12, 0x34, 0x12, 0x00, 0x00]; - let r = LocatorRecord::parse(&raw).unwrap(); - assert_eq!(r.number, 1); - assert_eq!(r.seeker, 0x1234_5678); - assert_eq!(r.size, 0x0000_1234); - } -} diff --git a/src/limits.rs b/src/limits.rs index aee7b9d7..70afc71b 100644 --- a/src/limits.rs +++ b/src/limits.rs @@ -147,44 +147,6 @@ impl ParseLimits { } } -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn paranoid_is_strictly_lower_than_safe() { - let safe = ParseLimits::safe(); - let paranoid = ParseLimits::paranoid(); - assert!(paranoid.max_handle_entries < safe.max_handle_entries); - assert!(paranoid.max_retained_dispatch_errors < safe.max_retained_dispatch_errors); - assert!(paranoid.max_xdata_iterations < safe.max_xdata_iterations); - assert!(paranoid.max_class_entries < safe.max_class_entries); - assert!(paranoid.max_lwpolyline_count < safe.max_lwpolyline_count); - assert!(paranoid.max_image_clip_verts < safe.max_image_clip_verts); - assert!(paranoid.max_leader_points < safe.max_leader_points); - assert!(paranoid.max_spline_count < safe.max_spline_count); - } - - #[test] - fn permissive_is_strictly_higher_than_safe() { - let safe = ParseLimits::safe(); - let permissive = ParseLimits::permissive(); - assert!(permissive.max_handle_entries > safe.max_handle_entries); - assert!(permissive.max_retained_dispatch_errors > safe.max_retained_dispatch_errors); - assert!(permissive.max_xdata_iterations > safe.max_xdata_iterations); - assert!(permissive.max_class_entries > safe.max_class_entries); - assert!(permissive.max_lwpolyline_count > safe.max_lwpolyline_count); - assert!(permissive.max_image_clip_verts > safe.max_image_clip_verts); - assert!(permissive.max_leader_points > safe.max_leader_points); - assert!(permissive.max_spline_count > safe.max_spline_count); - } - - #[test] - fn default_matches_safe() { - assert_eq!(ParseLimits::default(), ParseLimits::safe()); - } -} - /// Caller-configurable safety limits for graph iteration — the /// handle-driven walks that resolve owner chains, reactor back-refs, /// and block expansions. @@ -263,34 +225,6 @@ impl WalkerLimits { } } -#[cfg(test)] -mod walker_tests { - use super::*; - - #[test] - fn walker_paranoid_strictly_lower() { - let s = WalkerLimits::safe(); - let p = WalkerLimits::paranoid(); - assert!(p.max_handles < s.max_handles); - assert!(p.max_scan_bytes < s.max_scan_bytes); - assert!(p.max_block_nesting < s.max_block_nesting); - } - - #[test] - fn walker_permissive_strictly_higher() { - let s = WalkerLimits::safe(); - let p = WalkerLimits::permissive(); - assert!(p.max_handles > s.max_handles); - assert!(p.max_scan_bytes > s.max_scan_bytes); - assert!(p.max_block_nesting > s.max_block_nesting); - } - - #[test] - fn walker_default_matches_safe() { - assert_eq!(WalkerLimits::default(), WalkerLimits::safe()); - } -} - /// Top-level safety profile applied at [`crate::DwgFile::open_with_limits`]. /// /// Bundles the three load-bearing caps: @@ -365,34 +299,3 @@ impl OpenLimits { } } } - -#[cfg(test)] -mod open_limits_tests { - use super::*; - - #[test] - fn open_paranoid_strictly_lower_than_safe() { - let s = OpenLimits::safe(); - let p = OpenLimits::paranoid(); - assert!(p.max_file_bytes < s.max_file_bytes); - assert!(p.max_section_bytes < s.max_section_bytes); - // Inner profile caps follow ParseLimits + WalkerLimits invariants. - assert!(p.parse.max_handle_entries < s.parse.max_handle_entries); - assert!(p.walker.max_handles < s.walker.max_handles); - } - - #[test] - fn open_permissive_strictly_higher_than_safe() { - let s = OpenLimits::safe(); - let p = OpenLimits::permissive(); - assert!(p.max_file_bytes > s.max_file_bytes); - assert!(p.max_section_bytes > s.max_section_bytes); - assert!(p.parse.max_handle_entries > s.parse.max_handle_entries); - assert!(p.walker.max_handles > s.walker.max_handles); - } - - #[test] - fn open_default_matches_safe() { - assert_eq!(OpenLimits::default(), OpenLimits::safe()); - } -} diff --git a/src/lz77.rs b/src/lz77.rs index 8863c3c4..a2621443 100644 --- a/src/lz77.rs +++ b/src/lz77.rs @@ -379,238 +379,3 @@ fn read_long_compression_offset(r: &mut Lz77Reader<'_>) -> Result { } } } - -#[cfg(test)] -mod tests { - use super::*; - - /// Literal-only stream: literal length of 5 (encoded as 0x02 → +3), - /// 5 literal bytes "Hello", then terminator 0x11. - #[test] - fn literal_only_stream() { - let stream = [0x02, b'H', b'e', b'l', b'l', b'o', 0x11]; - let out = decompress(&stream, None).unwrap(); - assert_eq!(&out, b"Hello"); - } - - /// Initial literal length of 0 via high-nibble peekback: first byte - /// is 0x11 (opcode = terminator). Output is empty. - #[test] - fn empty_stream_via_immediate_terminator() { - let stream = [0x11]; - let out = decompress(&stream, None).unwrap(); - assert!(out.is_empty()); - } - - /// Simple back-reference with opcode 0x22 (compBytes = 4, class - /// 0x21-0x3F which gets +1 offset adjustment): - /// Literal "ABCDEFG" (len 7 = 0x04 + 3), copy 4 bytes at effective - /// offset 3 (= base 2 + 1), then 2 literal bytes "XY", terminator. - /// - /// Copy starts at position 7-3 = 4, copies out[4..4+4] = "EFGE" - /// (the 4th byte wraps since we're reading what we just wrote). - #[test] - fn back_reference_with_rle_wrap() { - // Two-byte offset: base=2, litCount=2 - // first = (2 << 2) | 2 = 0x0A - // second = 0 - // Class 0x21-0x3F adds +1 → effective offset = 3. - let stream = [ - 0x04, b'A', b'B', b'C', b'D', b'E', b'F', b'G', // literal run - 0x22, 0x0A, 0x00, // opcode 0x22 → copy 4 at offset 3, litCount 2 - b'X', b'Y', // 2 literal bytes - 0x11, // terminator - ]; - let out = decompress(&stream, None).unwrap(); - assert_eq!(&out, b"ABCDEFGEFGEXY"); - } - - /// Literal length extended via 0x00 run: `0x00 0x00 0x02` means - /// `0x0F + 0xFF + 0x02 + 3 = 275` bytes of literal. - #[test] - fn extended_literal_length() { - const N: usize = 275; - let mut stream = vec![0x00u8, 0x00, 0x02]; - for i in 0..N { - stream.push((i & 0xFF) as u8); - } - stream.push(0x11); - let out = decompress(&stream, None).unwrap(); - assert_eq!(out.len(), N); - for (i, &b) in out.iter().enumerate() { - assert_eq!(b, (i & 0xFF) as u8); - } - } - - /// Opcode 0x40-0xFF encoding: litCount in bits 0-1 of opcode1. Offset - /// is computed as `((op1 >> 2) & 3) | (op2 << 2) + 1` per the ACadSharp - /// reference (the spec's "+0" appears to be a typo). - /// - /// opcode1 = 0x41 = 0b0100_0001: - /// compBytes = ((0x41 & 0xF0) >> 4) - 1 = 3. - /// litCount = 0x41 & 0x03 = 1. - /// opcode2 = 0x01: - /// compOffset = ((0x41 >> 2) & 3) | (0x01 << 2) + 1 - /// = 0 | 4 + 1 = 5. - /// - /// Literal "ABCDEFG" (len 7), copy 3 at offset 5 → out[2..5] = "CDE", - /// then 1 literal "Z", then terminator. Total: "ABCDEFGCDEZ". - #[test] - fn opcode_0x40_family() { - let stream = [ - 0x04, b'A', b'B', b'C', b'D', b'E', b'F', b'G', // 7 literals - 0x41, 0x01, // compBytes=3, offset=5, litCount=1 - b'Z', 0x11, - ]; - let out = decompress(&stream, None).unwrap(); - assert_eq!(&out, b"ABCDEFGCDEZ"); - } - - /// Truncated input should error cleanly, not panic. - #[test] - fn truncated_stream_errors() { - let stream = [0x04, b'A', b'B']; // says 7 literals, only has 2 - let e = decompress(&stream, None).unwrap_err(); - assert!(matches!(e, Error::Lz77Truncated)); - } - - /// Reserved opcode range 0x00-0x0F is not valid AFTER the initial - /// literal length. Construct: 0 literal length (via peekback of 0x11 - /// is too trivial; use 0 via direct). We need a stream that reaches - /// the main loop with opcode < 0x10. - /// - /// Easiest: 1-byte literal (0x0F? but that's a valid literal length) - /// → use 0x01 = length 4, 4 literals, then opcode 0x05 (invalid). - #[test] - fn reserved_opcode_errors() { - let stream = [0x01, b'A', b'B', b'C', b'D', 0x05]; - let e = decompress(&stream, None).unwrap_err(); - assert!(matches!(e, Error::Lz77InvalidOpcode { opcode: 0x05, .. })); - } - - // ================================================================ - // Decompression-bomb defense regression tests. - // ================================================================ - - /// A literal run that asks for more bytes than the configured - /// `max_output_bytes` must error before any allocation. - #[test] - fn output_limit_rejects_oversize_literal_run() { - let limits = DecompressLimits { - max_output_bytes: 8, - max_backref_len: 8, - }; - // Literal length 8 → 0x05 + 3 = 8; 8 bytes of literal. - let mut stream = vec![0x05]; - stream.extend_from_slice(b"AAAAAAAA"); - stream.push(0x11); - // 8 bytes fits exactly; must succeed. - let out = decompress_with_limits(&stream, None, limits).unwrap(); - assert_eq!(out.len(), 8); - - // Nudge the literal length up one byte (0x06 + 3 = 9) and - // expect the limit to fire. - let mut bomb = vec![0x06]; - bomb.extend_from_slice(b"AAAAAAAAA"); - bomb.push(0x11); - let err = decompress_with_limits(&bomb, None, limits).unwrap_err(); - assert!(matches!(err, Error::Lz77OutputLimitExceeded { limit: 8 })); - } - - /// A back-reference copy whose length alone would exceed the - /// configured limit must error. - #[test] - fn output_limit_rejects_oversize_backref() { - // Build a stream: literal "AB" (len=2 → 0x00+3 via extension), - // then a 0x20 opcode that claims comp_bytes = 64 and offset = 2. - // Skip 0x20: uses the long-compression-offset extension. - // - // Simpler: use opcode 0x21 (class 0x21-0x3F) with a small cb. - // cb = (0x21 & 0x1F) + 2 = 1 + 2 = 3. Not useful for a big copy. - // - // Use opcode 0x20 which extends comp_bytes via the long form. - // cb = long_extension + 0x21. - // To hit cb = 100, long_extension = 79, encoded as single byte 0x4F. - // - // Layout: [literal_len=0x02 "ABC", terminator would be 0x11, but we - // need the main-loop opcode]. Easier: use the 0x40-0xFF family - // where cb = (op1>>4 - 1). E.g., op1=0xC1 → cb = (0xC >> 0) - 1 - // = 0xB = 11. Still small. Combined with class 0x20 is fastest. - let mut stream = vec![ - 0x01, b'A', b'B', b'C', b'D', // 4 literal bytes - 0x20, // class 0x20 → cb = long_extension + 0x21 - 0x4F, // long_extension first byte = 0x4F (79). cb = 79 + 0x21 = 112. - 0x00, 0x00, // two-byte offset, small value → offset = 0 + 1 = 1, lit=0 - 0x11, // terminator (won't be reached) - ]; - // Force literal-len-extension for the lit=0 case to avoid the - // reserved-opcode path; not needed since our terminator 0x11 is - // NOT in 0x00..=0x0F. - // (literal length 0 via peekback: the first post-opcode byte is - // the lit_count from the two-byte-offset, which is 0, then we - // read a real literal length. Use 0x11 as the peeked byte to - // terminate via the opcode path.) - stream.push(0x11); - - let limits = DecompressLimits { - max_output_bytes: 1024 * 1024, - max_backref_len: 50, // less than the 112-byte copy above - }; - let err = decompress_with_limits(&stream, None, limits).unwrap_err(); - assert!( - matches!( - err, - Error::Lz77BackrefTooLong { - length: 112, - limit: 50 - } - ), - "err={err:?}" - ); - } - - /// `expected_size` must be clamped to the output limit so a - /// caller-claimed huge size cannot trigger a giant up-front - /// `Vec::with_capacity` allocation. - #[test] - fn expected_size_is_clamped_to_output_limit() { - let limits = DecompressLimits { - max_output_bytes: 16, - max_backref_len: 16, - }; - // Decompressing the empty-terminator stream with a huge - // expected_size must not allocate 1 TiB. This test does not - // observe memory directly — it asserts behavior (no error, - // empty output) that proves the function didn't try to - // allocate more than the limit. - let stream = [0x11u8]; - let out = decompress_with_limits(&stream, Some(1usize << 40), limits).unwrap(); - assert!(out.is_empty()); - } - - /// The default `DecompressLimits` profile ships with a 256 MiB - /// output cap. This pins the contract so a future config tweak - /// has to consciously lower/raise it rather than drift unnoticed. - #[test] - fn default_limits_cap_output_at_256_mib() { - let d = DecompressLimits::default(); - assert_eq!(d.max_output_bytes, 256 * 1024 * 1024); - assert_eq!(d.max_backref_len, 1024 * 1024); - } - - /// Compression-bomb regression: a small input that falsely claims - /// a huge `expected_size` must not allocate anywhere near that - /// size. Caller-supplied hint gets clamped to the output limit, - /// and the decoded output is small and accurate — no panic, no - /// OOM, no over-read. - #[test] - fn small_input_with_huge_expected_size_stays_bounded() { - // 6-byte input decompresses to a 5-byte literal "HELLO". - // expected_size claims 1 TiB — must be clamped by the default - // DecompressLimits (256 MiB) and allocate accordingly. - let stream = [0x02, b'H', b'E', b'L', b'L', b'O', 0x11]; - let huge = 1usize << 40; - let out = decompress(&stream, Some(huge)).unwrap(); - assert_eq!(&out, b"HELLO"); - } -} diff --git a/src/metadata.rs b/src/metadata.rs index 63a26286..5cc85e63 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -515,50 +515,3 @@ impl<'a> ByteCursor<'a> { Ok(s) } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn summary_info_empty_strings() { - // 8 strings × 2 bytes (length=0) + 8+8+8 date bytes + 2 byte prop count - let mut data = vec![0u8; 2 * 8 + 24 + 2]; - // all zeros means empty strings and zero properties - let si = SummaryInfo::parse(&data).unwrap(); - assert_eq!(si.title, ""); - assert_eq!(si.properties.len(), 0); - // silence unused mut warning - data[0] = 0; - } - - #[test] - fn summary_info_with_title() { - // title = "X" (1 byte + NUL = 2 bytes with len=2) - let mut data = Vec::new(); - data.extend_from_slice(&2u16.to_le_bytes()); - data.extend_from_slice(b"X\0"); - for _ in 0..7 { - data.extend_from_slice(&0u16.to_le_bytes()); - } - data.extend_from_slice(&[0u8; 24]); - data.extend_from_slice(&0u16.to_le_bytes()); - let si = SummaryInfo::parse(&data).unwrap(); - assert_eq!(si.title, "X"); - } - - #[test] - fn preview_rejects_bad_sentinel() { - let bad = [0u8; 32]; - assert!(Preview::parse(&bad).is_err()); - } - - #[test] - fn preview_empty_after_sentinel_errors_cleanly() { - let mut data = Vec::new(); - data.extend_from_slice(&Preview::START_SENTINEL); - // too short, overall_size + image count require 5 more bytes - let out = Preview::parse(&data); - assert!(out.is_err()); - } -} diff --git a/src/object_type.rs b/src/object_type.rs index a5229eeb..b5e56955 100644 --- a/src/object_type.rs +++ b/src/object_type.rs @@ -433,53 +433,3 @@ impl fmt::Display for ObjectType { } } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn fixed_codes_decode() { - assert_eq!(ObjectType::from_code(0x13), ObjectType::Line); - assert_eq!(ObjectType::from_code(0x12), ObjectType::Circle); - assert_eq!(ObjectType::from_code(0x11), ObjectType::Arc); - assert_eq!(ObjectType::from_code(0x4D), ObjectType::LwPolyline); - assert_eq!(ObjectType::from_code(0x4E), ObjectType::Hatch); - } - - #[test] - fn proxy_codes_decode() { - assert_eq!(ObjectType::from_code(0x1F2), ObjectType::AcadProxyEntity); - assert_eq!(ObjectType::from_code(0x1F3), ObjectType::AcadProxyObject); - } - - #[test] - fn dynamic_codes_decode() { - assert_eq!(ObjectType::from_code(500), ObjectType::Custom(500)); - assert_eq!(ObjectType::from_code(12345), ObjectType::Custom(12345)); - } - - #[test] - fn entity_classification() { - assert!(ObjectType::Line.is_entity()); - assert!(ObjectType::Circle.is_entity()); - assert!(ObjectType::LwPolyline.is_entity()); - assert!(!ObjectType::Dictionary.is_entity()); - assert!(!ObjectType::LayerControl.is_entity()); - } - - #[test] - fn control_classification() { - assert!(ObjectType::LayerControl.is_control()); - assert!(ObjectType::BlockControl.is_control()); - assert!(!ObjectType::Layer.is_control()); - assert!(!ObjectType::Line.is_control()); - } - - #[test] - fn short_label_stable() { - assert_eq!(ObjectType::Line.short_label(), "LINE"); - assert_eq!(ObjectType::LwPolyline.short_label(), "LWPOLYLINE"); - assert_eq!(ObjectType::MText.short_label(), "MTEXT"); - } -} diff --git a/src/r2007.rs b/src/r2007.rs index 7728cc3b..692c8270 100644 --- a/src/r2007.rs +++ b/src/r2007.rs @@ -749,132 +749,3 @@ pub fn read_section( out.truncate(total); Ok(out) } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn system_page_size_matches_the_spec_pseudo_code() { - // §5.3.1's floor. - assert_eq!(system_page_size(0), 0x400); - assert_eq!(system_page_size(304), 0x400); - // The section map of every corpus R2007 file: 2132 decompressed - // bytes, which the writer put in a 0x1200-byte page. - assert_eq!(system_page_size(2132), 0x1200); - } - - #[test] - fn deinterleave_gathers_each_codewords_message_bytes() { - // Two interleaved codewords, message size 3: codeword 0 owns the - // even positions, codeword 1 the odd ones. - let mut page = vec![0u8; 2 * RS_CODEWORD]; - for i in 0..RS_CODEWORD { - page[i * 2] = i as u8; - page[i * 2 + 1] = 0x80u8.wrapping_add(i as u8); - } - let out = deinterleave(&page, 2, 3).unwrap(); - assert_eq!(out, vec![0, 1, 2, 0x80, 0x81, 0x82]); - } - - #[test] - fn deinterleave_refuses_a_short_page() { - let err = deinterleave(&[0u8; 10], 1, RS_SYSTEM_MESSAGE).unwrap_err(); - assert!(matches!(err, Error::Truncated { .. })); - } - - #[test] - fn page_map_accumulates_offsets_in_map_order() { - // Three pages of 0x400 / 0x200 / 0x100 bytes with ids 22, 23, 3 — - // the shape every corpus file starts with. - let mut data = Vec::new(); - for (size, id) in [(0x400i64, 22i64), (0x200, 23), (0x100, 3)] { - data.extend_from_slice(&size.to_le_bytes()); - data.extend_from_slice(&id.to_le_bytes()); - } - let map = PageMap::parse(&data).unwrap(); - assert_eq!(map.len(), 3); - assert_eq!(map.file_offset_of(22), Some(PAGE_BASE)); - assert_eq!(map.file_offset_of(23), Some(PAGE_BASE + 0x400)); - assert_eq!(map.file_offset_of(3), Some(PAGE_BASE + 0x600)); - assert_eq!(map.file_offset_of(99), None); - } - - #[test] - fn page_map_keys_on_the_absolute_id_so_free_pages_still_resolve() { - let mut data = Vec::new(); - data.extend_from_slice(&0x400i64.to_le_bytes()); - data.extend_from_slice(&(-7i64).to_le_bytes()); - let map = PageMap::parse(&data).unwrap(); - assert_eq!(map.file_offset_of(7), Some(PAGE_BASE)); - } - - /// Build one section-map record by hand and read it back. The name - /// length counts the two-byte terminator, which is the detail that - /// decides where the page list starts. - #[test] - fn section_map_name_length_counts_the_terminator() { - let name = "AcDb:Handles"; - let mut data = Vec::new(); - let mut header = [0u64; 8]; - header[0] = 591; // data size - header[1] = 63488; // max size - header[2] = 0; // encryption - header[3] = 0x3F6E_0450; // hash code - header[4] = (name.len() as u64 + 1) * 2; // name length, with terminator - header[6] = 4; // encoding - header[7] = 1; // one page - for v in header { - data.extend_from_slice(&v.to_le_bytes()); - } - for unit in name.encode_utf16() { - data.extend_from_slice(&unit.to_le_bytes()); - } - data.extend_from_slice(&0u16.to_le_bytes()); - for v in [0u64, 768, 14, 591, 566, 0x0A92_5B96, 0] { - data.extend_from_slice(&v.to_le_bytes()); - } - let map = SectionMap::parse(&data).unwrap(); - assert_eq!(map.sections.len(), 1); - let s = &map.sections[0]; - assert_eq!(s.name, "AcDb:Handles"); - assert_eq!(s.hash_code, 0x3F6E_0450); - assert_eq!(s.encoding, 4); - assert_eq!(s.pages.len(), 1); - assert_eq!(s.pages[0].id, 14); - assert_eq!(s.pages[0].compressed, 566); - assert_eq!(s.pages[0].uncompressed, 591); - } - - #[test] - fn section_map_drops_the_trailing_unnamed_record() { - let mut data = vec![0u8; 0x40]; - // A zero header: no name, no pages. SectionsAmount counts it, the - // parsed list does not. - LittleEndian::write_u64(&mut data[0..8], 0); - let map = SectionMap::parse(&data).unwrap(); - assert!(map.sections.is_empty()); - } - - #[test] - fn file_header_parse_refuses_a_short_buffer() { - let err = FileHeader::parse(&[0u8; 0x100]).unwrap_err(); - assert!(matches!(err, Error::Truncated { .. })); - } - - #[test] - fn encrypted_section_is_refused_rather_than_mis_decoded() { - let desc = SectionDescriptor { - name: "AcDb:AcDbObjects".into(), - data_size: 16, - max_size: 0xF800, - encryption: 1, - hash_code: 0x674C_05A9, - encoding: 4, - pages: Vec::new(), - }; - let err = - read_section(&[], &PageMap::default(), &desc, DecompressLimits::default()).unwrap_err(); - assert!(matches!(err, Error::SectionEncrypted { .. })); - } -} diff --git a/src/r21_lz.rs b/src/r21_lz.rs index 17f2c983..5b8d4b8d 100644 --- a/src/r21_lz.rs +++ b/src/r21_lz.rs @@ -362,109 +362,3 @@ pub fn decompress(src: &[u8], expected: usize, limits: DecompressLimits) -> Resu } Ok(out) } - -#[cfg(test)] -mod tests { - use super::*; - - /// The first sixteen bytes of `line_2007.dwg`'s Reed-Solomon-decoded - /// file-header payload, decompressed by hand from the spec: - /// - /// - `20 00 00 02` — leading opcode `0x20` (high nibble 2), so the - /// literal length is `src[3] & 7` = 2. - /// - literal `00 70` → permuted to `70 00` (header size `0x70`). - /// - opcode `0x70` → high nibble 7: length 7, offset 1 → seven zero - /// bytes, completing the `u64` `0x70`. - /// - `0x02 & 7` = 2 → literal `01 03` → permuted to `03 01`. - /// - opcode `0x58` → length 5, offset 9 → five zero bytes. - /// - /// Result: `70 00 00 00 00 00 00 00` (header size 112) followed by - /// `00 03 01 00 00 00 00 00` (file size 0x10300 = 66304, which is - /// `line_2007.dwg`'s exact byte count). - const HEADER_PREFIX: &[u8] = &[ - 0x20, 0x00, 0x00, 0x02, 0x00, 0x70, 0x70, 0x02, 0x01, 0x03, 0x58, 0x00, - ]; - - #[test] - fn header_prefix_decodes_to_the_two_documented_u64_fields() { - let out = decompress(HEADER_PREFIX, 16, DecompressLimits::default()).unwrap(); - assert_eq!(out.len(), 16); - assert_eq!(u64::from_le_bytes(out[0..8].try_into().unwrap()), 0x70); - assert_eq!(u64::from_le_bytes(out[8..16].try_into().unwrap()), 66304); - } - - #[test] - fn two_byte_literal_block_is_reversed() { - let mut out = Vec::new(); - emit_permuted(&[0xAA, 0xBB], 0, 2, &mut out).unwrap(); - assert_eq!(out, vec![0xBB, 0xAA]); - } - - #[test] - fn four_and_eight_byte_literal_blocks_are_straight() { - let src: Vec = (0..8).collect(); - let mut four = Vec::new(); - emit_permuted(&src, 0, 4, &mut four).unwrap(); - assert_eq!(four, vec![0, 1, 2, 3]); - let mut eight = Vec::new(); - emit_permuted(&src, 0, 8, &mut eight).unwrap(); - assert_eq!(eight, vec![0, 1, 2, 3, 4, 5, 6, 7]); - } - - #[test] - fn sixteen_byte_literal_block_swaps_its_halves() { - let src: Vec = (0..16).collect(); - let mut out = Vec::new(); - emit_permuted(&src, 0, 16, &mut out).unwrap(); - assert_eq!( - out, - vec![8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7] - ); - } - - /// The `AcDb:AppInfoHistory` measurement from the module docs: an - /// 11-byte literal whose `2 [9]` block must come out reversed for the - /// UTF-16 name to be well formed. - #[test] - fn eleven_byte_literal_reverses_only_its_two_byte_block() { - let src: Vec = (0..11).collect(); - let mut out = Vec::new(); - emit_permuted(&src, 0, 11, &mut out).unwrap(); - assert_eq!(out, vec![10, 9, 1, 2, 3, 4, 5, 6, 7, 8, 0]); - } - - #[test] - fn thirty_two_byte_block_swaps_its_halves_and_recurses() { - let src: Vec = (0..32).collect(); - let mut out = Vec::new(); - emit_permuted(&src, 0, 32, &mut out).unwrap(); - let expect: Vec = (24..32).chain(16..24).chain(8..16).chain(0..8).collect(); - assert_eq!(out, expect); - } - - #[test] - fn truncated_stream_errors_rather_than_panicking() { - let err = - decompress(&[0x20, 0x00, 0x00, 0x07], 64, DecompressLimits::default()).unwrap_err(); - assert!(matches!(err, Error::Lz77Truncated)); - } - - #[test] - fn output_cap_is_enforced() { - let limits = DecompressLimits { - max_output_bytes: 8, - ..DecompressLimits::default() - }; - let err = decompress(HEADER_PREFIX, 16, limits).unwrap_err(); - assert!(matches!(err, Error::Lz77OutputLimitExceeded { limit: 8 })); - } - - #[test] - fn empty_input_yields_empty_output() { - assert!( - decompress(&[], 0, DecompressLimits::default()) - .unwrap() - .is_empty() - ); - } -} diff --git a/src/reed_solomon.rs b/src/reed_solomon.rs index 986db192..8aa1d724 100644 --- a/src/reed_solomon.rs +++ b/src/reed_solomon.rs @@ -323,41 +323,3 @@ fn solve_magnitudes(syn: &[u8], positions: &[usize], f: &Field) -> Result Result> { // just to get a checksum; mirrors the shape of the public API. #[allow(unused_imports)] pub(crate) use crc::section_page_checksum as _unused_export; - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn page_map_empty_input() { - let pages = decode_page_map_entries(&[]).unwrap(); - assert!(pages.is_empty()); - } - - #[test] - fn page_map_single_positive_entry() { - // number = 1, size = 0x7400 - let mut data = Vec::new(); - data.extend_from_slice(&1i32.to_le_bytes()); - data.extend_from_slice(&0x7400u32.to_le_bytes()); - let pages = decode_page_map_entries(&data).unwrap(); - assert_eq!(pages.len(), 1); - assert_eq!(pages[0].number, 1); - assert_eq!(pages[0].size, 0x7400); - assert_eq!(pages[0].file_offset, 0x100); - assert!(!pages[0].is_gap); - } - - #[test] - fn page_map_gap_entry_consumes_four_trailing_u32s() { - // number = -2 (gap), size = 0x1000, then parent=9 left=10 right=11 end=0 - let mut data = Vec::new(); - data.extend_from_slice(&(-2i32).to_le_bytes()); - data.extend_from_slice(&0x1000u32.to_le_bytes()); - data.extend_from_slice(&9u32.to_le_bytes()); - data.extend_from_slice(&10u32.to_le_bytes()); - data.extend_from_slice(&11u32.to_le_bytes()); - data.extend_from_slice(&0u32.to_le_bytes()); - // then a valid positive entry to verify cursor advanced right - data.extend_from_slice(&3i32.to_le_bytes()); - data.extend_from_slice(&0x200u32.to_le_bytes()); - let pages = decode_page_map_entries(&data).unwrap(); - assert_eq!(pages.len(), 2); - assert_eq!(pages[0].number, -2); - assert!(pages[0].is_gap); - assert_eq!(pages[0].size, 0x1000); - assert_eq!(pages[1].number, 3); - assert_eq!(pages[1].file_offset, 0x100 + 0x1000); - } - - #[test] - fn section_info_empty_descriptions() { - // Header with NumDescriptions = 0 and 4 more u32 padding. - let mut data = Vec::new(); - data.extend_from_slice(&0u32.to_le_bytes()); - data.extend_from_slice(&2u32.to_le_bytes()); - data.extend_from_slice(&0x7400u32.to_le_bytes()); - data.extend_from_slice(&0u32.to_le_bytes()); - data.extend_from_slice(&0u32.to_le_bytes()); - let descs = decode_section_descriptions(&data).unwrap(); - assert!(descs.is_empty()); - } -}