crates/gunnar-pktline/tests/sideband.rs
//! Sideband tests.
//!
//! The band byte lives *inside* the payload, so a full sideband line carries
//! `MAX_PAYLOAD - 1` bytes of data. Getting that off by one produces lines one
//! byte over the protocol maximum, and only for payloads that land exactly on
//! a chunk boundary, which no small fixture will ever hit. Hence the multi-chunk
//! cases below are sized to straddle the boundary deliberately.
use gunnar_pktline::*;
fn enc(band: u8, data: &[u8]) -> Vec<u8> {
let mut out = Vec::new();
write_sideband(&mut out, band, data).expect("sideband write failed");
out
}
/// Split a sideband stream into its lines, asserting each one is well formed.
fn lines(wire: &[u8], band: u8) -> Vec<Vec<u8>> {
let mut rest = wire;
let mut out = Vec::new();
while !rest.is_empty() {
match decode(rest).expect("sideband line failed to decode") {
Decoded::Packet(Packet::Data(payload), n) => {
assert!(
payload.len() <= MAX_PAYLOAD,
"line payload {} exceeds MAX_PAYLOAD {MAX_PAYLOAD}",
payload.len()
);
assert!(
!payload.is_empty(),
"a sideband line always has its band byte"
);
assert_eq!(payload[0], band, "every line must be marked with the band");
out.push(payload[1..].to_vec());
rest = &rest[n..];
}
other => panic!("expected a data packet, got {other:?}"),
}
}
out
}
#[test]
fn a_short_sideband_line_is_byte_exact() {
assert_eq!(enc(BAND_PROGRESS, b"hi"), b"0007\x02hi".to_vec());
assert_eq!(enc(BAND_PACK, b"PACK"), b"0009\x01PACK".to_vec());
assert_eq!(
enc(BAND_ERROR, b"fatal: no\n"),
b"000f\x03fatal: no\n".to_vec()
);
}
#[test]
fn an_empty_sideband_write_emits_nothing_at_all() {
// Not a bare band byte, not a zero-length line: nothing. A lone band byte is
// a frame announcing no data; on band 3, an error message with no message.
assert!(enc(BAND_PACK, b"").is_empty());
assert!(enc(BAND_PROGRESS, b"").is_empty());
assert!(enc(BAND_ERROR, b"").is_empty());
}
#[test]
fn one_full_chunk_is_one_line_at_exactly_the_maximum() {
let data = vec![b'x'; SIDEBAND_CHUNK];
let wire = enc(BAND_PACK, &data);
assert_eq!(
&wire[..4],
b"fff0",
"a full sideband line is the largest legal line"
);
assert_eq!(wire.len(), MAX_LINE);
assert_eq!(lines(&wire, BAND_PACK), vec![data]);
}
#[test]
fn one_byte_past_a_chunk_becomes_two_lines() {
let data = vec![b'y'; SIDEBAND_CHUNK + 1];
let wire = enc(BAND_PROGRESS, &data);
let got = lines(&wire, BAND_PROGRESS);
assert_eq!(got.len(), 2);
assert_eq!(got[0].len(), SIDEBAND_CHUNK);
assert_eq!(got[1].len(), 1);
assert_eq!(got.concat(), data);
}
#[test]
fn an_exact_multiple_of_the_chunk_emits_no_trailing_empty_line() {
// A chunker written as "while remaining >= CHUNK { … }" plus a final flush
// emits a spurious zero-data line here, which reads as a band-marked packet
// carrying nothing.
let data = vec![b'z'; 2 * SIDEBAND_CHUNK];
let wire = enc(BAND_PACK, &data);
let got = lines(&wire, BAND_PACK);
assert_eq!(got.len(), 2);
assert_eq!(got[0].len(), SIDEBAND_CHUNK);
assert_eq!(got[1].len(), SIDEBAND_CHUNK);
assert_eq!(wire.len(), 2 * MAX_LINE);
assert_eq!(got.concat(), data);
}
#[test]
fn a_payload_over_twice_the_maximum_splits_reassembles_and_stays_in_band() {
let len = 2 * MAX_PAYLOAD + 7;
// Content varies per byte so a reassembly that reorders or duplicates a
// chunk cannot pass by looking identical.
let data: Vec<u8> = (0..len).map(|i| (i % 251) as u8).collect();
let wire = enc(BAND_PACK, &data);
let expected_lines = len.div_ceil(SIDEBAND_CHUNK);
assert_eq!(expected_lines, 3);
let got = lines(&wire, BAND_PACK);
assert_eq!(got.len(), expected_lines);
assert_eq!(got[0].len(), SIDEBAND_CHUNK);
assert_eq!(got[1].len(), SIDEBAND_CHUNK);
assert_eq!(got[2].len(), len - 2 * SIDEBAND_CHUNK);
assert_eq!(got.concat(), data, "reassembly must be byte-exact");
// Total on the wire: every line paid its own 4-byte header and band byte.
assert_eq!(wire.len(), len + 5 * expected_lines);
}
#[test]
fn bands_do_not_bleed_into_one_another() {
let mut wire = Vec::new();
write_sideband(&mut wire, BAND_PACK, b"packdata").unwrap();
write_sideband(&mut wire, BAND_PROGRESS, b"counting objects\n").unwrap();
write_sideband(&mut wire, BAND_PACK, b"more").unwrap();
write_flush(&mut wire).unwrap();
let mut d = Decoder::new();
d.push(&wire);
let packets = d.drain().unwrap();
assert_eq!(packets.len(), 4);
let mut pack = Vec::new();
let mut progress = Vec::new();
for p in &packets[..3] {
let payload = p.payload().unwrap();
match payload[0] {
BAND_PACK => pack.extend_from_slice(&payload[1..]),
BAND_PROGRESS => progress.extend_from_slice(&payload[1..]),
b => panic!("unexpected band {b}"),
}
}
assert_eq!(pack, b"packdatamore".to_vec());
assert_eq!(progress, b"counting objects\n".to_vec());
assert_eq!(packets[3], Packet::Flush);
}
#[test]
fn a_sideband_stream_survives_being_read_one_byte_at_a_time() {
let data: Vec<u8> = (0..3 * SIDEBAND_CHUNK / 2)
.map(|i| (i % 256) as u8)
.collect();
let wire = enc(BAND_PACK, &data);
let mut d = Decoder::new();
let mut reassembled = Vec::new();
for b in &wire {
d.push(&[*b]);
while let Some(p) = d.next_packet().unwrap() {
reassembled.extend_from_slice(&p.payload().unwrap()[1..]);
}
}
assert!(d.is_empty());
assert_eq!(reassembled, data);
}