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

nordisk/gunnar

crates/gunnar-wire/tests/emit_amplification.rs

//! **What the engine emitted, against what `select` chose.**
//!
//! `GitServe::emit_pack`'s first parameter is an **exact set** (DECIDED
//! 2026-08-10, `upload_pack.rs`): the engine must not close it over
//! reachability. It may still emit *more* objects than it was given, and
//! legitimately so — a packfile's `OFS_DELTA` names its base by a backwards
//! distance inside the pack, so an engine copying stored entries has to pull in
//! any delta base that fell outside the selection or emit something
//! unparseable. `Caps::thin` is the allowance to skip that, and an engine is
//! free not to take it.
//!
//! So the gap between the two numbers is real, expected, and **unbounded**, and
//! that is exactly why it has to be reported. MEASURED on the laptop 2026-08-10, the
//! `h2h-linear-sha1-2048c-1024f-16k` corpus over the znippy engine, a
//! negotiated fetch from a receiver `N` commits behind the tip:
//!
//! ```text
//!   N commits behind    select chose    engine emitted    amplification
//!                  1              18               157            8.72x
//!                256            4382              7801            1.78x
//! ```
//!
//! Nothing in gunnar could say so. [`UploadSummary::objects_sent`] carried the
//! right-hand column and there was no left-hand column anywhere: the selected
//! count reached exactly one place, a human-readable sideband progress string
//! ("gunnar: packing N objects from M commits") that `git fetch -q` suppresses
//! and no meter parses. `bench/`'s `gunnar.vs_git_push` then reported a 33.6%
//! receiver divergence with no instrument able to attribute it, and
//! `vs_git_fetch_divergent`'s `wire_out_ratio_x = 1.215` had the same cause and
//! the same silence.
//!
//! This is the shape — a published number describing a path other than
//! the one its name claims — so the test is a **count** and it is asserted, not
//! logged.

mod common;

use gix_hash::{Kind, ObjectId};
use gunnar_store::git_storage_trait::{
    Caps, Extent, GcReport, GitOps, GitServe, ObjType, Oid, PackStats, ReachSet, RefCas, RefRow,
    RefUpdate, Stored, TxId,
};
use gunnar_wire::upload_pack::FetchRequest;
use gunnar_wire::UploadPack;

use common::Fixture;

/// The fixture's five commits carry one blob and one tree each.
const OBJECTS_PER_COMMIT: usize = 3;
const COMMITS: usize = 5;

/// A `GitServe` whose `emit_pack` adds objects the caller did not select.
///
/// This is what a real engine does when it copies stored pack entries and has
/// to bring an unselected delta base along, and it is the *only* way to see the
/// two counts diverge in a wire test: [`gunnar_store::mem::MemGitServe`] has no
/// packs, so its `emit_pack` is entry-for-entry with its input and the gap this
/// suite exists to measure is structurally zero there.
///
/// It borrows rather than owns, so it lays over the fixture's own store for one
/// test without rebuilding the repository — the same shape as `common`'s
/// `Unindexed`.
struct ClosesOverBases<'a> {
    inner: &'a dyn GitServe,
    /// Appended to whatever `emit_pack` is handed. Objects that are in the
    /// store and are **not** in the selection, so the arithmetic is unambiguous.
    extra: Vec<ObjectId>,
}

impl GitOps for ClosesOverBases<'_> {
    fn put(&self, pack: &[u8], refs: &[RefUpdate]) -> anyhow::Result<TxId> {
        self.inner.put(pack, refs)
    }
    fn put_pack(&self, bytes: &[u8]) -> anyhow::Result<TxId> {
        self.inner.put_pack(bytes)
    }
    fn put_refs(&self, updates: &[RefUpdate]) -> anyhow::Result<TxId> {
        self.inner.put_refs(updates)
    }
    fn get(&self, oid: Oid<'_>) -> anyhow::Result<Option<Stored>> {
        self.inner.get(oid)
    }
    fn has(&self, oid: Oid<'_>) -> anyhow::Result<bool> {
        self.inner.has(oid)
    }
    fn size(&self, oid: Oid<'_>) -> anyhow::Result<Option<u64>> {
        self.inner.size(oid)
    }
    fn extents(&self, oids: &[Oid<'_>]) -> anyhow::Result<Vec<Option<Extent>>> {
        self.inner.extents(oids)
    }
    fn refs(&self) -> anyhow::Result<Vec<RefRow>> {
        self.inner.refs()
    }
    fn update_ref(
        &self,
        name: &str,
        old: Option<Oid<'_>>,
        new: Option<Oid<'_>>,
    ) -> anyhow::Result<TxId> {
        self.inner.update_ref(name, old, new)
    }
    fn put_refs_cas(&self, edits: &[RefCas<'_>]) -> anyhow::Result<TxId> {
        self.inner.put_refs_cas(edits)
    }
    fn reachable(&self, want: &[Oid<'_>], have: &[Oid<'_>]) -> anyhow::Result<Vec<Vec<u8>>> {
        self.inner.reachable(want, have)
    }
    fn gc(&self) -> anyhow::Result<GcReport> {
        self.inner.gc()
    }
}

impl GitServe for ClosesOverBases<'_> {
    fn read(&self, oid: Oid<'_>) -> anyhow::Result<Option<(ObjType, Vec<u8>)>> {
        self.inner.read(oid)
    }
    fn header(&self, oid: Oid<'_>) -> anyhow::Result<Option<(ObjType, u64)>> {
        self.inner.header(oid)
    }
    fn sizes(&self, oids: &[Oid<'_>]) -> anyhow::Result<Vec<Option<u64>>> {
        self.inner.sizes(oids)
    }
    fn head(&self) -> anyhow::Result<Option<RefRow>> {
        self.inner.head()
    }
    fn set_head(&self, target: &str) -> anyhow::Result<TxId> {
        self.inner.set_head(target)
    }
    fn select(&self, want: &[Oid<'_>], have: &[Oid<'_>]) -> anyhow::Result<Option<ReachSet>> {
        self.inner.select(want, have)
    }
    /// The format closure, simulated: emit the exact set **plus** bases the
    /// caller never asked for.
    fn emit_pack(
        &self,
        want: &[Oid<'_>],
        have: &[Oid<'_>],
        caps: &Caps,
        out: &mut dyn std::io::Write,
    ) -> anyhow::Result<PackStats> {
        let mut widened: Vec<Oid<'_>> = want.to_vec();
        for id in &self.extra {
            if !widened.iter().any(|other| *other == id.as_bytes()) {
                widened.push(id.as_bytes());
            }
        }
        self.inner.emit_pack(&widened, have, caps, out)
    }
}

/// A clone of `four`, the whole fixture history, with one unselected object
/// dragged in behind it.
///
/// **The assertion is a count and both halves of it matter.** `objects_sent`
/// alone cannot tell an engine that shipped the selection from one that shipped
/// nine times the selection; `objects_selected` alone cannot tell a correct
/// emit from a truncated one. Reported together they are a ratio, and the
/// ratio is what `bench/`'s head-to-head arms compare.
#[test]
fn a_fetch_reports_the_selected_set_beside_what_the_engine_emitted() {
    let fixture = Fixture::build(Kind::Sha1);
    // The annotated tag. It is in the store, it is reachable from nothing the
    // clone below wants (`include_tag` is off), so the selection cannot contain
    // it and the engine adding it is unambiguous.
    let store = ClosesOverBases {
        inner: &fixture.git,
        extra: vec![fixture.tag],
    };
    let upload = UploadPack::new(&store, fixture.kind);
    let mut out = Vec::new();
    let outcome = upload
        .fetch(
            &FetchRequest {
                wants: vec![fixture.four],
                done: true,
                thin_pack: true,
                ofs_delta: true,
                ..Default::default()
            },
            &mut out,
        )
        .expect("serve the clone");

    let selected = COMMITS * OBJECTS_PER_COMMIT;
    assert_eq!(
        outcome.objects_selected, selected,
        "`select` chose the five commits' {selected} objects; the summary must report that \
         number and not the engine's"
    );
    assert_eq!(
        outcome.objects_sent as usize,
        selected + 1,
        "the engine closed one unselected base into the pack, so it emitted one more object \
         than it was given"
    );
    assert!(
        outcome.objects_sent as usize > outcome.objects_selected,
        "the two counts must be distinguishable — a summary that reports only one of them \
         cannot tell a 1.0x emit from the measured 8.72x"
    );
}

/// The other direction, and the reason the first test is not enough on its own:
/// with no closure at all the two counts agree, and a field wired to the wrong
/// source would still pass the test above by accident.
#[test]
fn an_engine_that_adds_nothing_reports_the_two_counts_equal() {
    let fixture = Fixture::build(Kind::Sha1);
    let store = ClosesOverBases {
        inner: &fixture.git,
        extra: Vec::new(),
    };
    let upload = UploadPack::new(&store, fixture.kind);
    let mut out = Vec::new();
    let outcome = upload
        .fetch(
            &FetchRequest {
                wants: vec![fixture.four],
                done: true,
                thin_pack: true,
                ofs_delta: true,
                ..Default::default()
            },
            &mut out,
        )
        .expect("serve the clone");

    assert_eq!(outcome.objects_selected, COMMITS * OBJECTS_PER_COMMIT);
    assert_eq!(
        outcome.objects_sent as usize, outcome.objects_selected,
        "nothing was added, so `objects_sent` must equal `objects_selected`; a gap here is \
         the field reading the wrong source"
    );
}

/// A round that sends no packfile reports no selection either.
///
/// `FetchOutcome::default()` is returned from the negotiation rounds before the
/// last one, and a field that leaked a stale count into them would make
/// `UploadSummary`'s v2 accumulator — which **sums** over rounds — report a
/// multiple of the truth.
#[test]
fn a_negotiation_round_that_sends_no_pack_selects_nothing() {
    let fixture = Fixture::build(Kind::Sha1);
    let upload = UploadPack::new(&fixture.git, fixture.kind);
    let mut out = Vec::new();
    let outcome = upload
        .fetch(
            &FetchRequest {
                wants: vec![fixture.four],
                done: false,
                thin_pack: true,
                ofs_delta: true,
                ..Default::default()
            },
            &mut out,
        )
        .expect("serve one negotiation round");
    assert!(!outcome.sent_pack, "this round must not carry a packfile");
    assert_eq!(outcome.objects_sent, 0);
    assert_eq!(
        outcome.objects_selected, 0,
        "a round with no packfile has no selection to report"
    );
}