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

nordisk/edda

crates/gunnar-pack/tests/deflate_backend.rs

//! P-007's safety net: the zlib backend is `zlib-rs`, and swapping to it did
//! not trade compression quality for speed.
//!
//! The workspace selects `flate2`'s `zlib-rs` backend because it is ~1.9× faster
//! than the default `miniz_oxide` at effectively the same ratio, and is already
//! linked into this tree through `gix-zlib`. Two things could silently undo
//! that, and neither would fail any other test in the suite:
//!
//!   * **the feature comes off**: someone drops `features = ["zlib-rs"]` from
//!     the workspace `flate2` line, or a new crate pulls flate2 in a way that
//!     re-selects the C backend. Everything still works, just at half speed.
//!   * **the win was never free**. A backend that compresses *worse* would
//!     look faster on a benchmark while putting more bytes on every wire.
//!
//! So this file pins the one observable that separates the backends: the bytes
//! they produce. [`MINIZ_OXIDE_PACK_ID`] is the trailer of the pack below as
//! written by `miniz_oxide`, captured on 2026-08-03 by temporarily restoring
//! flate2's default features. If the backend ever reverts, the pack is
//! byte-identical to that one again and `backend_is_not_miniz_oxide` goes red.
//! It was seen red exactly that way before being committed.
//!
//! The constant is deliberately a *negative* pin. Pinning the zlib-rs output
//! instead would turn every zlib-rs patch release into a spurious failure,
//! which is how a guard gets deleted rather than fixed.

mod common;

use common::MemObjects;
use gix_hash::Kind;
use gunnar_pack::{write_pack_to_vec, WriteOptions};

/// The trailer of [`fixture_pack`] when `flate2` uses its default `miniz_oxide`
/// backend. MEASURED 2026-08-03 on oden; see the module docs for how.
const MINIZ_OXIDE_PACK_ID: &str =
    "bcda49c25e95816fd06a95aa1efb77299c14bac44837c4d1dce823364dedbad0";

/// Total size of [`fixture_pack`] at level 6, MEASURED 2026-08-03 on oden:
/// miniz_oxide **172_282** bytes, zlib-rs **172_308** bytes, i.e. **0.015%
/// apart**, which is P-007's "free win" claim reduced to two integers.
///
/// The band below is ±3%. Level 1 produces 280_823 bytes for this fixture
/// (+63%), so a silent level or backend-quality regression lands far outside it
/// while a zlib-rs patch release does not. `the_quality_band_can_fail` asserts
/// that gap rather than trusting this paragraph.
const EXPECTED_PACK_BYTES: usize = 172_308;
const BAND: f64 = 0.03;

/// Deterministic, source-code-shaped content: a fixed vocabulary assembled by a
/// seeded LCG, so every machine and every run builds the identical corpus.
///
/// Random *bytes* would be the wrong fixture. Incompressible input makes every
/// backend look alike and would hide precisely what this file is guarding.
fn fixture_blobs() -> Vec<Vec<u8>> {
    const WORDS: &[&str] = &[
        "let", "mut", "fn", "pub", "struct", "impl", "match", "self", "Result", "Ok", "Err",
        "writer", "hasher", "pack", "object", "deflate", "trailer", "bytes", "kind", "options",
        "gix_hash", "flate2", "compress", "scratch", "chunk", "return", "if", "else", "for",
        "while",
    ];
    let mut state: u64 = 0x9E37_79B9_7F4A_7C15;
    let mut next = move || {
        state = state
            .wrapping_mul(6364136223846793005)
            .wrapping_add(1442695040888963407);
        (state >> 33) as usize
    };

    (0..48)
        .map(|_| {
            let mut blob = String::with_capacity(16 * 1024);
            while blob.len() < 16 * 1024 {
                let indent = "    ".repeat(next() % 4);
                blob.push_str(&indent);
                for _ in 0..(4 + next() % 10) {
                    blob.push_str(WORDS[next() % WORDS.len()]);
                    blob.push(' ');
                }
                blob.push_str(";\n");
            }
            blob.into_bytes()
        })
        .collect()
}

/// The pack both constants above describe. SHA-256 so the fixture is
/// independent of which SHA-1 hasher `checksum.rs` picks; this file is about
/// the *compressor*, and mixing the two concerns would make a red ambiguous.
fn fixture_pack() -> (Vec<u8>, gunnar_pack::WriteOutcome) {
    let mut store = MemObjects::new(Kind::Sha256);
    let ids: Vec<_> = fixture_blobs()
        .into_iter()
        .map(|blob| store.insert_blob(blob))
        .collect();
    write_pack_to_vec(&ids, &store, &WriteOptions::new(Kind::Sha256)).expect("writing the fixture")
}

/// The backend is not `miniz_oxide`.
///
/// Two different zlib implementations do not produce the same deflate stream
/// for a 750 KiB corpus, so an equal pack id means the same backend produced
/// it. This is the assertion that catches the feature quietly falling off the
/// `flate2` line.
#[test]
fn backend_is_not_miniz_oxide() {
    let (_, outcome) = fixture_pack();
    assert_ne!(
        outcome.pack_id.to_string(),
        MINIZ_OXIDE_PACK_ID,
        "flate2 is compressing exactly like miniz_oxide again — the `zlib-rs` \
         feature has come off the workspace `flate2` dependency (P-007), and \
         deflate is running at about half speed"
    );
}

/// The swap did not cost compression quality.
///
/// P-007's whole claim is a free win: same bytes out, twice as fast. A backend
/// that got faster by compressing less would pass every correctness test in the
/// suite and quietly widen every clone.
#[test]
fn compression_quality_did_not_regress() {
    let (pack, outcome) = fixture_pack();
    assert_eq!(pack.len() as u64, outcome.bytes_written);

    let low = (EXPECTED_PACK_BYTES as f64 * (1.0 - BAND)) as usize;
    let high = (EXPECTED_PACK_BYTES as f64 * (1.0 + BAND)) as usize;
    assert!(
        (low..=high).contains(&pack.len()),
        "the fixture pack is {} bytes, outside the {low}..={high} band around the \
         measured {EXPECTED_PACK_BYTES}. Either the compression level moved or the \
         backend is compressing worse — P-007 was supposed to be free.",
        pack.len()
    );
}

/// **The red for the band.** Level 1 is the cheapest way to make the writer
/// "faster" while spending more bytes, and it must land outside the band the
/// test above accepts. Without this, a band wide enough to be quiet would also
/// be wide enough to be useless.
#[test]
fn the_quality_band_can_fail() {
    let mut store = MemObjects::new(Kind::Sha256);
    let ids: Vec<_> = fixture_blobs()
        .into_iter()
        .map(|blob| store.insert_blob(blob))
        .collect();
    let (fast, _) = write_pack_to_vec(
        &ids,
        &store,
        &WriteOptions::new(Kind::Sha256).compression_level(1),
    )
    .expect("writing at level 1");

    let high = (EXPECTED_PACK_BYTES as f64 * (1.0 + BAND)) as usize;
    assert!(
        fast.len() > high,
        "a level-1 pack is {} bytes, inside the band `compression_quality_did_not_regress` \
         accepts ({high} max) — that test cannot detect a quality regression and must be \
         retuned, not trusted",
        fast.len()
    );
}

/// Stock `git`, with its own zlib rather than ours, inflates every entry of
/// what this backend produced.
///
/// `zlib-rs` emits a different bit stream from `miniz_oxide`; that is expected
/// and legal, but "legal" has to be checked against an implementation that has
/// no shared code with the one under test. `--strict` makes `index-pack` verify
/// each object's id against its inflated content, so this fails if the
/// compressor ever produces something that merely round-trips through itself.
#[test]
fn stock_git_inflates_what_this_backend_wrote() {
    // A hard failure, not a skip (`S-026`). "The real interop coverage lives in
    // `gunnar-wire`" was the old justification for returning early here, and it
    // is not one: on an image without `git` this test reported a pass having
    // inflated nothing, and so did every suite in `gunnar-wire`.
    let git = git_binary().expect("this test requires `git` on PATH");

    let dir = tempfile::tempdir().expect("tempdir");
    let (pack, outcome) = fixture_pack();
    let path = dir.path().join("in.pack");
    std::fs::write(&path, &pack).expect("writing the pack");

    let out = std::process::Command::new(git)
        .args(["index-pack", "--strict", "--object-format=sha256", "-v"])
        .arg(&path)
        .output()
        .expect("running git index-pack");
    assert!(
        out.status.success(),
        "stock git rejected a pack this backend wrote: {}",
        String::from_utf8_lossy(&out.stderr)
    );
    assert!(
        dir.path().join("in.idx").exists(),
        "git index-pack reported success but wrote no index — check output, not exit code"
    );
    assert_eq!(outcome.num_objects, 48);
}

fn git_binary() -> Option<&'static str> {
    std::process::Command::new("git")
        .arg("--version")
        .output()
        .ok()
        .filter(|out| out.status.success())
        .map(|_| "git")
}