Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/znippy-zoomies

linflate/tests/real_world_deflate.rs

//! Real-input regression tests for the DEFLATE decoder — the coverage that
//! `tests::inflate_real_jar_entry` in `src/lib.rs` only pretended to give.
//!
//! ## Why this file exists
//!
//! Until 2026-08-01 linflate's ONLY real-world proof was a unit test that read
//! its fixture from `/tmp/xml_class_compressed.bin` and answered a missing file
//! with a bare `return`. The file did not exist, its name appeared nowhere else
//! in the tree, so nothing ever created it — and cargo counts a bare `return`
//! as a pass:
//!
//! ```text
//! test tests::inflate_real_jar_entry ... ok
//! test result: ok. 1 passed; 0 failed; 0 ignored; ... finished in 0.00s
//! ```
//!
//! Two distinct defects, both fixed here:
//!
//! 1. **No real-input regression test existed at all.** linflate is a
//!    *decompressor*; the bug class this hid is a stream that decodes to the
//!    WRONG BYTES and returns `Ok`.
//! 2. **An attacker-choosable path fed a test ORACLE.** `/tmp` is
//!    world-writable and the name was predictable, so any local user could
//!    decide what "correct output" meant. Every input below is under the
//!    repo's own control, reached through `CARGO_MANIFEST_DIR` or embedded in
//!    this file. Nothing here reads a world-writable path, and neither test
//!    has a precondition it can skip on.
//!
//! ## The oracles — never linflate's own output
//!
//! A decoder compared against its own previous output can be wrong-but-
//! consistent forever, so neither number below comes from linflate:
//!
//! * `real_gnu_gzip_stream_matches_gzip_own_crc32` decodes a DEFLATE stream
//!   written by a DIFFERENT implementation — GNU gzip 1.14, i.e. the
//!   zlib-family encoder that also writes real JAR entries — and checks the
//!   result against the CRC-32 and ISIZE that **gzip itself** put in the
//!   trailer. The CRC routine used to check it is calibrated first against the
//!   published CRC-32 check value, so a broken oracle reports red instead of
//!   agreeing with a broken decoder.
//! * `real_jar_members_match_original_bytes` packs this crate's own source
//!   files into a genuine ZIP/JAR (real local headers, real central directory,
//!   real EOCD), parses that container back the way a JAR reader does, and
//!   compares each inflated member against **the original file bytes** —
//!   ground truth by construction — and then against `miniz_oxide::inflate`,
//!   an independent decoder that is already a dev-dependency.
//!
//! Both tests carry a NON-VACUITY FLOOR: an empty scan is red, not green.

use std::path::{Path, PathBuf};

// ── Oracle helpers ───────────────────────────────────────────────────────────

/// Bitwise CRC-32 (IEEE 802.3, reflected, poly 0xEDB88320) — the checksum both
/// gzip and ZIP store. Table-free on purpose: it is an oracle, so it is short
/// enough to audit by eye, and `calibrate_crc32` proves it before use.
fn crc32(data: &[u8]) -> u32 {
    let mut crc = !0u32;
    for &b in data {
        crc ^= b as u32;
        for _ in 0..8 {
            let mask = (crc & 1).wrapping_neg();
            crc = (crc >> 1) ^ (0xEDB8_8320 & mask);
        }
    }
    !crc
}

/// LAW 2 — never trust an oracle you have not seen fail. `CRC32("123456789")`
/// is the check value published with the algorithm; if `crc32` is broken this
/// fires before it can rubber-stamp a broken decode.
fn calibrate_crc32() {
    let got = crc32(b"123456789");
    assert_eq!(
        got, 0xCBF4_3926,
        "CRC-32 ORACLE IS BROKEN: check value for \"123456789\" came out 0x{got:08x}, \
         expected 0xcbf43926 — every comparison below would have been meaningless"
    );
}

fn unhex(s: &str) -> Vec<u8> {
    let digits: Vec<u8> = s.bytes().filter(|b| !b.is_ascii_whitespace()).collect();
    assert!(
        digits.len().is_multiple_of(2),
        "embedded fixture has an odd number of hex digits ({})",
        digits.len()
    );
    digits
        .chunks(2)
        .map(|p| {
            let hi = (p[0] as char).to_digit(16).expect("hex digit");
            let lo = (p[1] as char).to_digit(16).expect("hex digit");
            (hi * 16 + lo) as u8
        })
        .collect()
}

fn u16le(b: &[u8], at: usize) -> usize {
    u16::from_le_bytes([b[at], b[at + 1]]) as usize
}

fn u32le(b: &[u8], at: usize) -> u32 {
    u32::from_le_bytes([b[at], b[at + 1], b[at + 2], b[at + 3]])
}

// ── Test 1: a stream produced by GNU gzip, checked against gzip's own CRC ────

/// `linflate/README.md` at commit 01d0f2f, compressed by real GNU gzip 1.14 on
/// oden with `gzip -9 -n -c` (`-n` so the stream carries no name and no
/// timestamp and is therefore byte-stable). Embedded as hex rather than
/// committed as a binary blob: this tree keeps binaries in Git-LFS and raw
/// blobs out of git, and a text fixture stays diffable and reviewable.
///
/// Regenerate with:
/// ```text
/// gzip -9 -n -c linflate/README.md | od -An -tx1 -v | tr -d ' \n'
/// ```
/// Nothing in the test depends on the *content* being today's README — the
/// trailer that gzip wrote is the oracle, so the fixture is self-verifying and
/// never goes stale.
const GNU_GZIP_README_HEX: &str = "\
     1f8b0800000000000203ad56e14e1b4710fe7f4f310d5265bb3e4c5020d4a454\
     2618c515046468520545f6fa6eeddbb0debdeeee61ecba517f55eadfaaafd017\
     cb937466f76c0c212aa8b59038fb76be9df9e69b6f770da45043c91c8fa2da93\
     9694439672a3c0720357cc7ce03010ee490d3efdf6179c671cfc0a97e15bcbb9\
     057ec5cd9496ac47d121b30ef2c2f0b85be0d341fbf0a875de8694277a9c1b6e\
     ad3650293703ae4642f1ea3ab4af9d6189e3290c8d1e4717f20333b1b1ef2b99\
     73b96d361a894ef9809bd1ba36a386d22615f6b2315322cfa755601618d88c19\
     8c4f0c0117161f0798937619a28d668f00532994db63396b6b70c0ad18a9288a\
     a1563b2ca48c07c57088a58f1145d66a50916290725f50d533a4345867381b0b\
     35c227ca67cc920c2badd3bb4498a490ccc044a8544f3cee59e7f80017b9248b\
     139d4f097586b0b11a05c8b3b3f666a3f5e6a74d48b2425d022e1248fc10c9d4\
     48be64794e9b1dbd7bfe1c062cb98c0dc714b94ab8f5f8fb86a92493483f6c3f\
     8bb153d42dc045425209b4c533100ad32e1227b4b27518150c831cf5f7d31f7f\
     6f6d5304f23c74587a08f4c8e719569ac652274c42ff60d9e67336c0edfa906b\
     bdd861c68d868cb31c98a4f5b411e01f0a0932edbc46520e392acb433f7dea33\
     1d0a635d2c5164125e21f5638621845ec2da62e0bf82d4fab2c8518e5220f5cc\
     ab4953b24cc6136d645ab6255072c64763ae5ccc26a89b12aa5f0ab367c3cb4a\
     b58f311a31193286395fa2a20a953294fb44b84c1744e2cf8530c4fefe61e775\
     ebc883bff5ad8d7322ea1a33cc736d5cb909579e1a1c1294a57553ccdce69c24\
     e1c415956f901eac35d01145a7384c40c3e4d55328cb861c6a29cfb94ab1c3a8\
     83da3afc6811b11f5ef6b193d825853053cf805717fe86595a91d888246e18a6\
     a7fd4260066b197327125cb56c07d2998709d8c76db2313397517486854a8e22\
     c59cca7eb9cce86294e5c80536f34c606d82a1424d5e58a85cef6cc7dbcfea40\
     e2ad36a3680e2ffd84ced14896717378ad1d16308fe671f82cfeaf3ee35ba476\
     e154c8e61c3e3edfd880e3fd0686c2f06636eb7067a00819fc44195afa716b6b\
     19b61cd5328856de4c34545e5629607bebfe7d9673e6e31046cc7afa5aa454e0\
     c7a7df2e83c811c1772e74f2c62028ce6fb5099595f8ea1d8089c119f7bd2201\
     dedec937e9b418486c60ebb41345fd7edfe03611dae0d2d99bcd5f16eac6aeeb\
     3adcd1facd0f4ef7ae7852879337edeedb6ee7bcdd7bd56e1d744f4e8eebd009\
     6bdac668f3eb6e14351ad06593a5cd7ffafd4f40d1c7e580930907b3acd0f087\
     5690b668c6abd150c16a4a958577f0b4095f5f143befeb80138602c1af639409\
     fd5485780fbadc16d2bd28ac98f1db39ed859c5e6b1587710cf3dd5c39805067\
     5a71d0e4e0d8143fd6b6fea5395f4d72e10aff5f9e6f71f0826b87c3004ac7a0\
     a95d3185c65d53f83ca91ef9512f845722c0cf43b2ac971bf624574d0879460f\
     4bfd084d961a1c30a1422923529163733dc9682577d236b6fa2f99f76440fd6f\
     15804779643d2fb5bae24ad02c3717071456415eb9aa9dd502c2a0dc27077e8d\
     cdc33a7ab45d99c86a1a6f78f2a2d8d9bb3f11a20cc549d72796a6e8afc2a295\
     29bccd4c2923a7bd4597bc97f3b5b479ba0e4cd0cf83dbb86994e069eeee99e5\
     32ab5d320b3290353c43d888df9887e4eef30ec077d0a8d1e1b11cf9e06450c1\
     b369341379835c964e796c77156a8d5d8f43dd2a13fe0e90b3af2e368a9ddddb\
     34c1372b5ef579beef911d82a2e21c578873b3fa0b2e520f32091b57bfdf8dc2\
     d33ade721475b75262559724c029331830f40ccb7888162da7ab87161ec6b9b6\
     fcde938a9cfc420e9084cd875f37e7b46518121f0901e551f7df158c1f5addc6\
     bbce2932eb2fd474c30a808fb902afe0514be1f6c18b38feec3d0b17ee850e6e\
     dfe9cb43e948245c5914d571e71c4ebad0caf122cce3cdf50dbc4fc771147539\
     d2299c36d326bc7840727bd13fc3f656fcac0c0000\
     ";

/// Split a gzip member into (raw DEFLATE payload, trailer CRC-32, trailer ISIZE).
/// RFC 1952 §2.3 — the two trailer values are written by the ENCODER, which is
/// exactly why they make an independent oracle here.
fn split_gzip(gz: &[u8]) -> (&[u8], u32, u32) {
    assert!(gz.len() > 18, "gzip fixture too short: {} bytes", gz.len());
    assert_eq!(
        (gz[0], gz[1], gz[2]),
        (0x1f, 0x8b, 0x08),
        "embedded fixture is not a DEFLATE gzip member"
    );
    let flg = gz[3];
    let mut at = 10;
    if flg & 0b0000_0100 != 0 {
        // FEXTRA
        at += 2 + u16le(gz, at);
    }
    if flg & 0b0000_1000 != 0 {
        // FNAME
        while gz[at] != 0 {
            at += 1;
        }
        at += 1;
    }
    if flg & 0b0001_0000 != 0 {
        // FCOMMENT
        while gz[at] != 0 {
            at += 1;
        }
        at += 1;
    }
    if flg & 0b0000_0010 != 0 {
        // FHCRC
        at += 2;
    }
    let end = gz.len() - 8;
    (&gz[at..end], u32le(gz, end), u32le(gz, end + 4))
}

#[test]
fn real_gnu_gzip_stream_matches_gzip_own_crc32() {
    calibrate_crc32();

    let gz = unhex(GNU_GZIP_README_HEX);
    let (deflate, want_crc, want_isize) = split_gzip(&gz);

    // ── NON-VACUITY FLOOR: an empty fixture must be red, not green. ─────────
    assert!(
        deflate.len() >= 512 && want_isize >= 1024,
        "NON-VACUITY FLOOR: embedded GNU-gzip fixture decodes to nothing worth \
         checking (payload {} bytes, ISIZE {want_isize}) — this test would have \
         asserted NOTHING about real DEFLATE input",
        deflate.len()
    );

    let expected_len = want_isize as usize;
    let mut out = vec![0u8; expected_len + linflate::OVERWRITE_HEADROOM];
    let written = linflate::inflate_into(deflate, &mut out)
        .unwrap_or_else(|e| panic!("linflate failed on a real GNU gzip stream: {e:?}"));
    out.truncate(written);

    assert_eq!(
        written, expected_len,
        "LENGTH MISMATCH vs the ISIZE GNU gzip wrote: inflated {written} bytes, \
         gzip says the original was {expected_len}"
    );

    let got_crc = crc32(&out);
    if got_crc != want_crc {
        let miniz = miniz_oxide::inflate::decompress_to_vec(deflate)
            .expect("miniz_oxide could not inflate the fixture either");
        let pos = out
            .iter()
            .zip(miniz.iter())
            .position(|(a, b)| a != b)
            .unwrap_or_else(|| out.len().min(miniz.len()));
        panic!(
            "WRONG BYTES from a real GNU gzip stream: CRC-32 0x{got_crc:08x} but gzip \
             wrote 0x{want_crc:08x}. First divergence from miniz_oxide at byte {pos} \
             (lens {} vs {})",
            out.len(),
            miniz.len()
        );
    }

    // Second, fully independent decoder over the same bytes.
    let miniz = miniz_oxide::inflate::decompress_to_vec(deflate)
        .expect("miniz_oxide could not inflate a stream GNU gzip produced");
    assert_eq!(
        out,
        miniz,
        "linflate and miniz_oxide disagree on a real GNU gzip stream ({} vs {} bytes)",
        out.len(),
        miniz.len()
    );

    assert!(
        !out.is_empty(),
        "NON-VACUITY FLOOR: decoded 0 bytes from the GNU gzip fixture"
    );
    eprintln!(
        "[linflate] real GNU gzip stream: {} compressed -> {} bytes, CRC-32 \
         0x{want_crc:08x} matches gzip's own trailer",
        deflate.len(),
        out.len()
    );
}

// ── Test 2: a genuine JAR built from this crate's own real source files ──────

struct Member {
    name: String,
    data: Vec<u8>,
    /// ZIP method: 8 = deflate, 0 = store.
    method: u16,
    comp: Vec<u8>,
}

/// Every input comes from `$CARGO_MANIFEST_DIR`, i.e. from the repository — the
/// crate's own sources, always present whenever cargo can run this test at all.
fn real_corpus(crate_dir: &Path) -> Vec<(String, Vec<u8>)> {
    let mut files: Vec<PathBuf> = Vec::new();
    for sub in ["src", "benches", "tests"] {
        let dir = crate_dir.join(sub);
        if let Ok(rd) = std::fs::read_dir(&dir) {
            for e in rd.flatten() {
                let p = e.path();
                if p.extension().and_then(|s| s.to_str()) == Some("rs") {
                    files.push(p);
                }
            }
        }
    }
    for top in ["Cargo.toml", "README.md", "CLAUDE.md"] {
        let p = crate_dir.join(top);
        if p.is_file() {
            files.push(p);
        }
    }
    // Deterministic order, so a failure names the same member on every run.
    files.sort();

    let mut out = Vec::new();
    for p in files {
        let bytes = std::fs::read(&p).unwrap_or_else(|e| panic!("read {}: {e}", p.display()));
        if bytes.is_empty() {
            continue;
        }
        let rel = p.strip_prefix(crate_dir).unwrap_or(&p);
        out.push((
            format!("org/nordisk/linflate/{}", rel.display()).replace('\\', "/"),
            bytes,
        ));
    }
    out
}

fn push_u16(v: &mut Vec<u8>, x: u16) {
    v.extend_from_slice(&x.to_le_bytes());
}
fn push_u32(v: &mut Vec<u8>, x: u32) {
    v.extend_from_slice(&x.to_le_bytes());
}

/// Build a real ZIP/JAR: local file headers, deflated members, central
/// directory, EOCD (PKWARE APPNOTE §4.3). `unzip -t` accepts the result.
fn build_jar(members: &[Member]) -> Vec<u8> {
    let mut zip = Vec::new();
    let mut offsets = Vec::with_capacity(members.len());

    for m in members {
        offsets.push(zip.len() as u32);
        push_u32(&mut zip, 0x0403_4b50); // local file header signature
        push_u16(&mut zip, 20); // version needed
        push_u16(&mut zip, 0); // flags
        push_u16(&mut zip, m.method);
        push_u16(&mut zip, 0); // dos time
        push_u16(&mut zip, 0x0021); // dos date = 1980-01-01
        push_u32(&mut zip, crc32(&m.data));
        push_u32(&mut zip, m.comp.len() as u32);
        push_u32(&mut zip, m.data.len() as u32);
        push_u16(&mut zip, m.name.len() as u16);
        push_u16(&mut zip, 0); // extra len
        zip.extend_from_slice(m.name.as_bytes());
        zip.extend_from_slice(&m.comp);
    }

    let cd_start = zip.len() as u32;
    for (m, &off) in members.iter().zip(offsets.iter()) {
        push_u32(&mut zip, 0x0201_4b50); // central directory signature
        push_u16(&mut zip, 20); // version made by
        push_u16(&mut zip, 20); // version needed
        push_u16(&mut zip, 0); // flags
        push_u16(&mut zip, m.method);
        push_u16(&mut zip, 0);
        push_u16(&mut zip, 0x0021);
        push_u32(&mut zip, crc32(&m.data));
        push_u32(&mut zip, m.comp.len() as u32);
        push_u32(&mut zip, m.data.len() as u32);
        push_u16(&mut zip, m.name.len() as u16);
        push_u16(&mut zip, 0); // extra len
        push_u16(&mut zip, 0); // comment len
        push_u16(&mut zip, 0); // disk number start
        push_u16(&mut zip, 0); // internal attrs
        push_u32(&mut zip, 0); // external attrs
        push_u32(&mut zip, off);
        zip.extend_from_slice(m.name.as_bytes());
    }
    let cd_size = zip.len() as u32 - cd_start;

    push_u32(&mut zip, 0x0605_4b50); // EOCD
    push_u16(&mut zip, 0);
    push_u16(&mut zip, 0);
    push_u16(&mut zip, members.len() as u16);
    push_u16(&mut zip, members.len() as u16);
    push_u32(&mut zip, cd_size);
    push_u32(&mut zip, cd_start);
    push_u16(&mut zip, 0); // comment len
    zip
}

/// One entry as a JAR reader recovers it: from the central directory, then
/// through the LOCAL header (whose name/extra lengths decide where the
/// compressed bytes actually begin).
struct ParsedEntry {
    name: String,
    method: u16,
    crc: u32,
    uncompressed: usize,
    data_range: std::ops::Range<usize>,
}

fn parse_jar(zip: &[u8]) -> Vec<ParsedEntry> {
    // EOCD: scan backwards for the signature, exactly as a real reader does.
    let eocd = (0..=zip.len().saturating_sub(22))
        .rev()
        .find(|&i| u32le(zip, i) == 0x0605_4b50)
        .expect("no end-of-central-directory record — the container is not a ZIP");
    let count = u16le(zip, eocd + 10);
    let cd_off = u32le(zip, eocd + 16) as usize;

    let mut at = cd_off;
    let mut out = Vec::with_capacity(count);
    for _ in 0..count {
        assert_eq!(
            u32le(zip, at),
            0x0201_4b50,
            "central directory entry at {at} has a bad signature"
        );
        let method = u16::from_le_bytes([zip[at + 10], zip[at + 11]]);
        let crc = u32le(zip, at + 16);
        let comp_size = u32le(zip, at + 20) as usize;
        let uncompressed = u32le(zip, at + 24) as usize;
        let name_len = u16le(zip, at + 28);
        let extra_len = u16le(zip, at + 30);
        let comment_len = u16le(zip, at + 32);
        let local_off = u32le(zip, at + 42) as usize;
        let name = String::from_utf8(zip[at + 46..at + 46 + name_len].to_vec()).expect("utf8 name");

        assert_eq!(
            u32le(zip, local_off),
            0x0403_4b50,
            "local header for {name} has a bad signature"
        );
        let l_name = u16le(zip, local_off + 26);
        let l_extra = u16le(zip, local_off + 28);
        let start = local_off + 30 + l_name + l_extra;

        out.push(ParsedEntry {
            name,
            method,
            crc,
            uncompressed,
            data_range: start..start + comp_size,
        });
        at += 46 + name_len + extra_len + comment_len;
    }
    out
}

#[test]
fn real_jar_members_match_original_bytes() {
    calibrate_crc32();

    let crate_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
    let corpus = real_corpus(crate_dir);

    // ── NON-VACUITY FLOOR #1, before any decode. The corpus is this crate's
    // own committed sources; if it is empty or tiny, the repo checkout — not
    // the decoder — is what is broken, and a silent pass here is the exact
    // defect this file replaces. ─────────────────────────────────────────────
    let corpus_bytes: usize = corpus.iter().map(|(_, d)| d.len()).sum();
    assert!(
        corpus.len() >= 6 && corpus_bytes >= 40 * 1024,
        "NOT COVERED: real_jar_members_match_original_bytes found only {} file(s) / \
         {corpus_bytes} bytes of real input under {} — expected the crate's own sources. \
         (This test asserted NOTHING on this run.)",
        corpus.len(),
        crate_dir.display()
    );

    // Members, exactly as a JAR has them.
    let mut members = Vec::new();
    // Real JARs open with a STORED manifest; keeps the container honest and
    // makes the reader dispatch on `method` rather than assume DEFLATE.
    let manifest = b"Manifest-Version: 1.0\r\nCreated-By: linflate test\r\n\r\n".to_vec();
    members.push(Member {
        name: "META-INF/MANIFEST.MF".to_string(),
        comp: manifest.clone(),
        data: manifest,
        method: 0,
    });

    // One entry per real source file, cycling the encoder level so the members
    // differ in block layout, not just in content.
    for (i, (name, data)) in corpus.iter().enumerate() {
        let level = [1u8, 6, 9][i % 3];
        members.push(Member {
            name: name.clone(),
            comp: miniz_oxide::deflate::compress_to_vec(data, level),
            data: data.clone(),
            method: 8,
        });
    }

    // Plus one BIG member: every source concatenated. Real data, > 64 KiB, so
    // the stream spans multiple DEFLATE blocks and its LZ77 matches reach back
    // across the 32 KiB window — the case single small members never touch.
    let mut all = Vec::with_capacity(corpus_bytes);
    for (_, d) in &corpus {
        all.extend_from_slice(d);
    }
    members.push(Member {
        name: "org/nordisk/linflate/all-sources.txt".to_string(),
        comp: miniz_oxide::deflate::compress_to_vec(&all, 9),
        data: all,
        method: 8,
    });

    let jar = build_jar(&members);
    let entries = parse_jar(&jar);
    assert_eq!(
        entries.len(),
        members.len(),
        "the JAR we built did not parse back to the same entry count"
    );

    let mut deflate_members = 0usize;
    let mut bytes_decoded = 0usize;
    let mut stored_members = 0usize;

    for e in &entries {
        let comp = &jar[e.data_range.clone()];
        let original = &members
            .iter()
            .find(|m| m.name == e.name)
            .unwrap_or_else(|| panic!("entry {} vanished from the member list", e.name))
            .data;

        if e.method == 0 {
            assert_eq!(
                comp,
                original.as_slice(),
                "stored member {} differs",
                e.name
            );
            stored_members += 1;
            continue;
        }

        // ── linflate, the code under test ──
        let mut out = vec![0u8; e.uncompressed + linflate::OVERWRITE_HEADROOM];
        let written = linflate::inflate_into(comp, &mut out)
            .unwrap_or_else(|err| panic!("inflate error on JAR member {}: {err:?}", e.name));
        out.truncate(written);

        // ── Oracle A: the original file bytes. Ground truth by construction. ──
        if out.as_slice() != original.as_slice() {
            let pos = out
                .iter()
                .zip(original.iter())
                .position(|(a, b)| a != b)
                .unwrap_or_else(|| out.len().min(original.len()));
            panic!(
                "WRONG BYTES for JAR member {}: mismatch at byte {pos} \
                 (got 0x{:02x}, original 0x{:02x}; lens {} vs {})",
                e.name,
                out.get(pos).copied().unwrap_or(0),
                original.get(pos).copied().unwrap_or(0),
                out.len(),
                original.len()
            );
        }
        assert_eq!(
            written, e.uncompressed,
            "JAR member {} decoded {written} bytes, its header says {}",
            e.name, e.uncompressed
        );

        // ── Oracle B: the CRC-32 recorded in the container. ──
        let got_crc = crc32(&out);
        assert_eq!(
            got_crc, e.crc,
            "CRC-32 mismatch for JAR member {}: 0x{got_crc:08x} vs 0x{:08x} in the header",
            e.name, e.crc
        );

        // ── Oracle C: an independent decoder over the same compressed bytes. ──
        let miniz = miniz_oxide::inflate::decompress_to_vec(comp)
            .unwrap_or_else(|err| panic!("miniz_oxide failed on member {}: {err:?}", e.name));
        assert_eq!(
            out,
            miniz,
            "linflate and miniz_oxide disagree on JAR member {} ({} vs {} bytes)",
            e.name,
            out.len(),
            miniz.len()
        );

        assert!(
            !out.is_empty(),
            "NON-VACUITY FLOOR: member {} decoded to 0 bytes",
            e.name
        );
        deflate_members += 1;
        bytes_decoded += out.len();
    }

    // ── NON-VACUITY FLOOR #2: a scan that compared nothing is RED. ──────────
    assert!(
        deflate_members >= 6,
        "NON-VACUITY FLOOR: only {deflate_members} DEFLATE member(s) were compared — \
         a real JAR scan that decodes almost nothing is not coverage. \
         (This test asserted essentially NOTHING on this run.)"
    );
    assert!(
        bytes_decoded >= 80 * 1024,
        "NON-VACUITY FLOOR: only {bytes_decoded} bytes were decoded and compared — \
         expected at least 80 KiB of real source through the decoder. \
         (This test asserted almost NOTHING on this run.)"
    );
    assert_eq!(stored_members, 1, "the stored manifest member went missing");

    eprintln!(
        "[linflate] real JAR: {} bytes of container, {deflate_members} DEFLATE members, \
         {bytes_decoded} bytes decoded and byte-compared against the original files, \
         their ZIP CRC-32s and miniz_oxide",
        jar.len()
    );
}