use std::cell::Cell;
use std::future::Future;
use std::net::SocketAddr;
use std::pin::pin;
use std::rc::Rc;
use std::task::Poll;
use std::time::Duration;
use slither::constants::{DEAD_TIMEOUT, MAX_PLAINTEXT, NO_ERROR};
use slither::error::{ConnectionLost, WriteError};
use slither::testutil::{FlakyPolicy, Pair, Tap, TestRecvStream, TestSendStream, local, settle};
const PATIENCE: Duration = Duration::from_secs(5);
const RECOVERY_PATIENCE: Duration = Duration::from_secs(12);
const NOT_BEFORE: Duration = Duration::from_millis(200);
async fn within_for<F: Future>(fut: F, what: &str, budget: Duration) -> F::Output {
match tokio::time::timeout(budget, fut).await {
Ok(v) => v,
Err(_) => panic!("{what}: still pending after {budget:?} of virtual time"),
}
}
async fn within<F: Future>(fut: F, what: &str) -> F::Output {
within_for(fut, what, PATIENCE).await
}
async fn recovering<F: Future>(fut: F, what: &str) -> F::Output {
within_for(fut, what, RECOVERY_PATIENCE).await
}
async fn is_pending<F: Future>(fut: F) -> bool {
tokio::time::timeout(NOT_BEFORE, fut).await.is_err()
}
async fn poll_once<F: Future>(mut fut: std::pin::Pin<&mut F>) -> Poll<F::Output> {
std::future::poll_fn(|cx| Poll::Ready(fut.as_mut().poll(cx))).await
}
fn payload(len: usize) -> Vec<u8> {
(0..len).map(|i| (i % 251) as u8).collect()
}
fn assert_same_bytes(got: &[u8], want: &[u8], what: &str) {
assert_eq!(
got.len(),
want.len(),
"{what}: byte count differs — a receiver that appends a retransmitted \
range instead of ignoring the overlap is long; one that drops the \
range it already had part of is short"
);
if let Some(i) = got.iter().zip(want.iter()).position(|(a, b)| a != b) {
panic!(
"{what}: first differing byte at offset {i}: got {:#04x}, want {:#04x}",
got[i], want[i]
);
}
}
fn sent_from(tap: &Tap, who: SocketAddr) -> usize {
tap.snapshot().iter().filter(|s| s.src == who).count()
}
fn blackholed(pair: &Pair) -> usize {
pair.net.sends() - pair.net.tap().len()
}
async fn write_all(s: &mut TestSendStream, buf: &[u8], what: &str) {
let mut done = 0usize;
while done < buf.len() {
let n = recovering(s.write(&buf[done..]), what)
.await
.unwrap_or_else(|e| panic!("{what}: write failed with {e:?}"));
assert!(
n >= 1,
"{what}: §16.2 — `Ok(0)` means only that `buf` was empty; a blocked \
write is `Pending`"
);
assert!(
n <= buf.len() - done,
"{what}: write claimed {n} bytes of a {}-byte buffer",
buf.len() - done
);
done += n;
}
}
async fn read_to_end(r: &mut TestRecvStream, what: &str) -> Vec<u8> {
let mut out = Vec::new();
let mut buf = vec![0u8; 4096];
loop {
match recovering(r.read(&mut buf), what).await {
Ok(Some(n)) => {
assert!(
n >= 1,
"{what}: §16.2 — `Ok(Some(0))` means only that `buf` was empty; \
no data available is `Pending`"
);
out.extend_from_slice(&buf[..n]);
}
Ok(None) => break,
Err(e) => panic!(
"{what}: expected a clean end of stream, got {e:?}. A \
`ConnectionLost` here is **ruling 128's post-death drain**, not \
this story: data that arrived before the death stays readable, \
and this read happens after the sender closed."
),
}
}
out
}
fn arm_drops(
tap: &Tap,
wire: &slither::testutil::SharedWire,
who: SocketAddr,
mut policy: FlakyPolicy,
skip: usize,
count: usize,
) -> usize {
let base = sent_from(tap, who);
policy.drop_at = (base + skip..base + skip + count).collect();
wire.set_policy(policy);
base + skip + count - 1
}
fn min_packets(len: usize) -> usize {
len.div_ceil(MAX_PLAINTEXT)
}
#[tokio::test(start_paused = true)]
async fn s12_a_bulk_stream_survives_loss_reordering_and_duplication() {
local(async {
let pair = Pair::seeded(0x5121_0501);
let (ca, cb) = pair.establish().await;
let tap = pair.net.tap();
let flaky = FlakyPolicy::lossy(0.10)
.with_delay(Duration::from_millis(20), Duration::from_millis(15))
.with_duplication(0.05);
let last_dropped = arm_drops(
&tap,
&pair.a.wire,
pair.a.addr(),
flaky.clone(),
4,
6,
);
pair.b.wire.set_policy(flaky);
const LEN: usize = 192 * 1024;
let want = payload(LEN);
let mut send = within(ca.open_uni(), "open_uni").await.expect("open_uni");
write_all(&mut send, &want, "S12 bulk write").await;
let mut recv = within(cb.accept_uni(), "accept_uni")
.await
.expect("accept_uni");
recovering(send.finish(), "finish").await.expect("finish");
settle().await;
let got = read_to_end(&mut recv, "S12 read to end").await;
assert_same_bytes(&got, &want, "S12 stream contents across a lossy path");
assert!(
matches!(
recovering(recv.read(&mut [0u8; 64]), "S12 second read").await,
Ok(None)
),
"§16.2: every read after the end of stream is `Ok(None)` — a \
non-sticky build hangs a reader that loops until it sees it twice"
);
let a_sends = sent_from(&tap, pair.a.addr());
assert!(
a_sends > last_dropped,
"the drop window ends at send index {last_dropped} and A's wire only \
reached {a_sends}: the six index-drops never happened, so this test \
proved nothing about loss recovery. `FlakyPolicy::lossy`'s own drops \
are invisible to every counter the public API has (the tap is \
written above the loss draw), which is why the pin is the index \
window and not the rate."
);
assert!(
a_sends > min_packets(LEN),
"A sent {a_sends} datagrams for a payload needing at least {} — with \
ten per cent loss in both directions a build that never retransmits \
cannot even have tried",
min_packets(LEN)
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn s12_a_stream_lost_before_anything_was_acked_is_rescued_by_the_pto() {
local(async {
let pair = Pair::seeded(0x5122_0502);
let (ca, cb) = pair.establish().await;
let delay = FlakyPolicy::perfect().with_delay(Duration::from_millis(10), Duration::ZERO);
pair.a.wire.set_policy(delay.clone());
pair.b.wire.set_policy(delay);
let want = payload(2000);
let lost_before = blackholed(&pair);
pair.net.block_path(pair.a.addr(), pair.b.addr());
let mut send = within(ca.open_uni(), "open_uni").await.expect("open_uni");
write_all(&mut send, &want, "pre-blackhole write").await;
settle().await;
within(send.finish(), "finish").await.expect("finish");
settle().await;
let lost_now = blackholed(&pair);
assert!(
lost_now > lost_before,
"the blackhole destroyed nothing: `Network::sends()` and the tap moved \
together, so every datagram this stream sealed left the fabric \
intact and there is no loss for §13 to recover from"
);
pair.net.heal_path(pair.a.addr(), pair.b.addr());
let mut recv = recovering(cb.accept_uni(), "accept_uni after the blackhole")
.await
.expect("accept_uni");
let got = read_to_end(&mut recv, "the PTO-rescued stream").await;
assert_same_bytes(&got, &want, "§13.3: the probe recovered the whole stream");
assert!(
lost_now - lost_before >= min_packets(want.len()),
"only {} datagrams were blackholed for a {}-byte payload — the hole \
was not the whole stream, so an ack-driven build could have \
recovered it without ever arming a probe",
lost_now - lost_before,
want.len()
);
let mut ca_closed = pin!(ca.closed());
let mut cb_closed = pin!(cb.closed());
assert!(
poll_once(ca_closed.as_mut()).await.is_pending(),
"§13.3 / ruling 33: an unanswered probe train ends at `DEAD_TIMEOUT` \
({DEAD_TIMEOUT:?}), not at the first probe"
);
assert!(poll_once(cb_closed.as_mut()).await.is_pending());
})
.await;
}
#[tokio::test(start_paused = true)]
async fn s12_a_stream_completes_when_the_acknowledgements_are_lost() {
local(async {
let pair = Pair::seeded(0x5123_0503);
let (ca, cb) = pair.establish().await;
let delay = FlakyPolicy::perfect().with_delay(Duration::from_millis(10), Duration::ZERO);
pair.a.wire.set_policy(delay.clone());
pair.b.wire.set_policy(delay);
let chunk1 = payload(4096);
let chunk2 = payload(4096);
let chunk3 = payload(4096);
let mut send = within(ca.open_uni(), "open_uni").await.expect("open_uni");
write_all(&mut send, &chunk1, "chunk 1").await;
settle().await;
let mut recv = within(cb.accept_uni(), "accept_uni")
.await
.expect("accept_uni");
let lost_before = blackholed(&pair);
pair.net.block_path(pair.b.addr(), pair.a.addr());
write_all(&mut send, &chunk2, "chunk 2 (acknowledgement blackholed)").await;
settle().await;
tokio::time::advance(Duration::from_millis(1500)).await;
settle().await;
let lost_now = blackholed(&pair);
assert!(
lost_now > lost_before,
"the receiver sent no acknowledgement into the blackhole: with \
nothing destroyed there is no lost-ACK state to survive, and this \
test would pass a build that never probes"
);
{
let mut ca_closed = pin!(ca.closed());
let mut cb_closed = pin!(cb.closed());
assert!(
poll_once(ca_closed.as_mut()).await.is_pending(),
"§18.1: a silent return path is not a connection error before \
`DEAD_TIMEOUT`"
);
assert!(poll_once(cb_closed.as_mut()).await.is_pending());
}
pair.net.heal_path(pair.b.addr(), pair.a.addr());
write_all(&mut send, &chunk3, "chunk 3 (after the heal)").await;
recovering(send.finish(), "finish").await.expect("finish");
settle().await;
let got = read_to_end(&mut recv, "the whole stream after an ACK outage").await;
let mut want = chunk1.clone();
want.extend_from_slice(&chunk2);
want.extend_from_slice(&chunk3);
assert_same_bytes(
&got,
&want,
"§9.5: a range the peer already holds may be retransmitted, and must \
not be delivered twice",
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn s12_the_probe_train_backs_off_and_the_transfer_completes_on_heal() {
local(async {
let pair = Pair::seeded(0x5124_0504);
let (ca, cb) = pair.establish().await;
let delay = FlakyPolicy::perfect().with_delay(Duration::from_millis(10), Duration::ZERO);
pair.a.wire.set_policy(delay.clone());
pair.b.wire.set_policy(delay);
let chunk1 = payload(4096);
let chunk2 = payload(8192);
let mut send = within(ca.open_uni(), "open_uni").await.expect("open_uni");
write_all(&mut send, &chunk1, "chunk 1").await;
settle().await;
let mut recv = within(cb.accept_uni(), "accept_uni")
.await
.expect("accept_uni");
for _ in 0..8 {
tokio::time::advance(Duration::from_millis(10)).await;
settle().await;
}
pair.net.block_path(pair.a.addr(), pair.b.addr());
write_all(&mut send, &chunk2, "chunk 2 (blackholed)").await;
settle().await;
let baseline = blackholed(&pair);
let start = tokio::time::Instant::now();
let step = Duration::from_millis(5);
let samples = 1600usize;
let mut probe_times: Vec<Duration> = Vec::new();
let mut seen = baseline;
for _ in 0..samples {
tokio::time::advance(step).await;
settle().await;
let now = blackholed(&pair);
while seen < now {
probe_times.push(tokio::time::Instant::now().duration_since(start));
seen += 1;
}
if probe_times.len() >= 4 {
break;
}
}
assert!(
probe_times.len() >= 3,
"§13.3: with nothing acknowledged and packets outstanding the PTO must \
arm and re-arm; only {} probe(s) left the wire in {:?} of virtual \
time. A build that disarms the probe when the path goes quiet stops \
here.",
probe_times.len(),
step * u32::try_from(samples).expect("sample count fits in u32")
);
let first = probe_times[1] - probe_times[0];
let later = probe_times[2] - probe_times[1];
assert!(
later > first,
"§13.3: consecutive unanswered probes double — the intervals were \
{first:?} then {later:?}. All-equal intervals are what a build whose \
`pto_count` resets on its own *sends* produces, and that build \
completes the transfer after the heal exactly like a correct one, so \
completion alone separates nothing."
);
assert!(
later * 2 >= first * 3,
"§13.3: the second interval should be about twice the first \
(`2^pto_count`); got {first:?} then {later:?}"
);
pair.net.heal_path(pair.a.addr(), pair.b.addr());
recovering(send.finish(), "finish").await.expect("finish");
settle().await;
let got = read_to_end(&mut recv, "the transfer after the blackhole healed").await;
let mut want = chunk1.clone();
want.extend_from_slice(&chunk2);
assert_same_bytes(&got, &want, "§13: the blackholed range is retransmitted");
})
.await;
}
#[tokio::test(start_paused = true)]
async fn s12_a_retransmitting_stream_does_not_starve_its_sibling() {
local(async {
let pair = Pair::seeded(0x5125_0505);
let (ca, cb) = pair.establish().await;
let tap = pair.net.tap();
let base = FlakyPolicy::perfect().with_delay(Duration::from_millis(5), Duration::ZERO);
pair.b.wire.set_policy(base.clone());
let last_dropped = arm_drops(
&tap,
&pair.a.wire,
pair.a.addr(),
base,
2,
12,
);
let big = payload(96 * 1024);
let small = payload(2048);
let mut a_send = within(ca.open_uni(), "open A").await.expect("open A");
write_all(&mut a_send, &big, "A bulk").await;
recovering(a_send.finish(), "A finish")
.await
.expect("finish");
let mut b_send = within(ca.open_uni(), "open B").await.expect("open B");
write_all(&mut b_send, &small, "B payload").await;
recovering(b_send.finish(), "B finish")
.await
.expect("finish");
settle().await;
let mut r_a = recovering(cb.accept_uni(), "accept A")
.await
.expect("accept A");
let mut r_b = recovering(cb.accept_uni(), "accept B")
.await
.expect("accept B");
assert_eq!(
r_a.id(),
a_send.id(),
"ruling 112: the first `accept_uni` yields the first stream opened"
);
assert_eq!(r_b.id(), b_send.id());
let origin = tokio::time::Instant::now();
let (a_done, b_done) = tokio::join!(
async {
let got = read_to_end(&mut r_a, "A read to end").await;
(got, tokio::time::Instant::now().duration_since(origin))
},
async {
let got = read_to_end(&mut r_b, "B read to end").await;
(got, tokio::time::Instant::now().duration_since(origin))
}
);
assert_same_bytes(&a_done.0, &big, "A's contents after recovery");
assert_same_bytes(&b_done.0, &small, "B's contents");
assert!(
b_done.1 < a_done.1,
"S13: stream B carries 2 KiB and stream A 96 KiB with a twelve-packet \
hole to retransmit — B must finish first. B finished at {:?} and A at \
{:?}, which is what a sender that services A's retransmission queue \
to exhaustion before rotating produces.",
b_done.1,
a_done.1
);
let a_sends = sent_from(&tap, pair.a.addr());
assert!(
a_sends > last_dropped,
"A's wire reached only index {a_sends}; the twelve-packet hole at \
index {last_dropped} was never made and there was nothing to \
retransmit"
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn s28_a_stream_acked_before_the_close_does_not_lose_its_tail() {
local(async {
let pair = Pair::seeded(0x5281_0501);
let (ca, cb) = pair.establish().await;
let tap = pair.net.tap();
let flaky = FlakyPolicy::lossy(0.20).with_delay(Duration::from_millis(10), Duration::ZERO);
let last_dropped = arm_drops(
&tap,
&pair.a.wire,
pair.a.addr(),
flaky.clone(),
1,
2,
);
pair.b.wire.set_policy(flaky);
let want = payload(4096);
let mut send = within(ca.open_uni(), "open_uni").await.expect("open_uni");
write_all(&mut send, &want, "S28 write").await;
settle().await;
let mut recv = recovering(cb.accept_uni(), "accept_uni")
.await
.expect("accept_uni");
recovering(send.finish(), "finish").await.expect("finish");
assert_eq!(
recovering(send.acked(), "SendStream::acked").await,
Ok(()),
"§16.2:4424 / ruling 47: `acked()` after `finish()` is legal and \
expected, and never reports `WriteError::Finished`"
);
ca.close(NO_ERROR, b"").await;
settle().await;
let got = read_to_end(&mut recv, "the peer's copy after the close").await;
assert_same_bytes(
&got,
&want,
"S28: every byte written before `acked()` resolved is at the peer, \
and the FIN with it",
);
let a_sends = sent_from(&tap, pair.a.addr());
assert!(
a_sends > last_dropped,
"A's wire reached index {a_sends}, short of the drop window ending at \
{last_dropped}: nothing was destroyed, so `close()` had nothing to \
lose and this test would pass the pre-ruling-47 ordering"
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn s28_connection_acked_covers_every_stream_before_the_close() {
local(async {
let pair = Pair::seeded(0x5282_0502);
let (ca, cb) = pair.establish().await;
let tap = pair.net.tap();
let flaky = FlakyPolicy::lossy(0.10).with_delay(Duration::from_millis(10), Duration::ZERO);
let last_dropped = arm_drops(
&tap,
&pair.a.wire,
pair.a.addr(),
flaky.clone(),
1,
2,
);
pair.b.wire.set_policy(flaky);
let first = payload(4096);
let second = payload(4096);
let mut s1 = within(ca.open_uni(), "open s1").await.expect("open s1");
write_all(&mut s1, &first, "s1 write").await;
recovering(s1.finish(), "s1 finish").await.expect("finish");
let mut s2 = within(ca.open_uni(), "open s2").await.expect("open s2");
write_all(&mut s2, &second, "s2 write").await;
recovering(s2.finish(), "s2 finish").await.expect("finish");
settle().await;
let mut r1 = recovering(cb.accept_uni(), "accept s1")
.await
.expect("accept");
let mut r2 = recovering(cb.accept_uni(), "accept s2")
.await
.expect("accept");
assert_eq!(
recovering(ca.acked(), "Connection::acked").await,
Ok(()),
"rulings 47/54: the snapshot spans every stream, and both were \
written before the call"
);
ca.close(NO_ERROR, b"").await;
settle().await;
assert_same_bytes(
&read_to_end(&mut r1, "s1 after the close").await,
&first,
"S28: stream 1 carried the injected hole and must still be whole",
);
assert_same_bytes(
&read_to_end(&mut r2, "s2 after the close").await,
&second,
"S28: stream 2 as well",
);
let a_sends = sent_from(&tap, pair.a.addr());
assert!(
a_sends > last_dropped,
"A's wire reached index {a_sends}, short of the drop window ending at \
{last_dropped}: no hole was made in stream 1, so a snapshot covering \
only stream 2 would have passed"
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn s28_connection_acked_on_an_empty_snapshot_resolves_at_once_live_and_dead() {
local(async {
let pair = Pair::seeded(0x5283_0503);
let (ca, cb) = pair.establish().await;
{
let mut live = pin!(ca.acked());
assert_eq!(
poll_once(live.as_mut()).await,
Poll::Ready(Ok(())),
"CONTRACT-5b §2.5: an empty snapshot is settled by definition and \
resolves on the first poll"
);
}
cb.close(NO_ERROR, b"bye").await;
settle().await;
assert!(
matches!(
within(ca.closed(), "closed").await,
ConnectionLost::PeerClosed { .. }
),
"the second half is only a test if the connection really died"
);
let mut dead = pin!(ca.acked());
assert_eq!(
poll_once(dead.as_mut()).await,
Poll::Ready(Ok(())),
"ruling 135: both `acked()` verbs answer from their settled snapshot \
**before** the death latch. Nothing was ever written, so there is \
nothing the death can have prevented — reporting `ConnectionLost` \
here is ruling 121's misreport with its sign flipped"
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn s28_a_fully_acknowledged_transfer_does_not_report_connection_lost() {
local(async {
let pair = Pair::seeded(0x5284_0504);
let (ca, cb) = pair.establish().await;
let want = payload(4096);
let mut send = within(ca.open_uni(), "open_uni").await.expect("open_uni");
write_all(&mut send, &want, "write").await;
within(send.finish(), "finish").await.expect("finish");
settle().await;
let mut recv = within(cb.accept_uni(), "accept_uni")
.await
.expect("accept_uni");
let got = read_to_end(&mut recv, "the peer's copy").await;
assert_same_bytes(&got, &want, "the transfer completed before the close");
cb.close(NO_ERROR, b"done").await;
settle().await;
assert!(
matches!(
within(ca.closed(), "closed").await,
ConnectionLost::PeerClosed { .. }
),
"the assertions below are only tests if the connection is dead"
);
assert_eq!(
within(send.acked(), "SendStream::acked after the death").await,
Ok(()),
"ruling 135 / CONTRACT-5b §2.5 outcome 2: this half reached \
`DataRecvd` — every byte and the FIN acknowledged — and a stream \
that completed did not un-complete when the connection died"
);
assert_eq!(
within(ca.acked(), "Connection::acked after the death").await,
Ok(()),
"ruling 135: the connection verb answers from the same settled \
snapshot, before the same latch"
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn s28_an_unacknowledged_transfer_on_a_dead_connection_reports_the_loss() {
local(async {
let pair = Pair::seeded(0x5285_0505);
let (ca, cb) = pair.establish().await;
let lost_before = blackholed(&pair);
pair.net.block_path(pair.a.addr(), pair.b.addr());
let mut send = within(ca.open_uni(), "open_uni").await.expect("open_uni");
write_all(&mut send, &payload(4096), "write into the blackhole").await;
settle().await;
within(send.finish(), "finish").await.expect("finish");
settle().await;
assert!(
blackholed(&pair) > lost_before,
"nothing was destroyed: with the bytes safely delivered the snapshot \
would settle and this test would assert the opposite of what it says"
);
cb.close(NO_ERROR, b"bye").await;
settle().await;
assert!(matches!(
within(ca.closed(), "closed").await,
ConnectionLost::PeerClosed { .. }
));
assert_eq!(
within(send.acked(), "SendStream::acked on a dead connection").await,
Err(WriteError::ConnectionLost(ConnectionLost::PeerClosed {
code: NO_ERROR,
reason: b"bye".to_vec(),
})),
"CONTRACT-5b §2.5 outcome 3: nothing was acknowledged, so the death \
latch is the answer — and it must carry the peer's code and reason, \
not a bare `TimedOut`"
);
assert_eq!(
within(ca.acked(), "Connection::acked on a dead connection").await,
Err(ConnectionLost::PeerClosed {
code: NO_ERROR,
reason: b"bye".to_vec(),
}),
"ruling 135 does not make `acked()` blind to the death — only to a \
death that arrived after the snapshot had already settled"
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn s28_a_parked_acked_wakes_when_the_connection_dies() {
local(async {
let pair = Pair::seeded(0x528a_050a);
let (ca, cb) = pair.establish().await;
let lost_before = blackholed(&pair);
pair.net.block_path(pair.a.addr(), pair.b.addr());
let mut send = within(ca.open_uni(), "open_uni").await.expect("open_uni");
write_all(&mut send, &payload(4096), "write into the blackhole").await;
settle().await;
within(send.finish(), "finish").await.expect("finish");
settle().await;
assert!(
blackholed(&pair) > lost_before,
"the bytes must be unacknowledgeable, or the futures below are not \
parked and the wakeup under test never happens"
);
let mut stream_acked = pin!(send.acked());
let mut conn_acked = pin!(ca.acked());
assert!(
poll_once(stream_acked.as_mut()).await.is_pending(),
"nothing can be acknowledged across a blackholed path"
);
assert!(poll_once(conn_acked.as_mut()).await.is_pending());
cb.close(NO_ERROR, b"bye").await;
settle().await;
assert_eq!(
within(stream_acked, "the parked SendStream::acked at the death").await,
Err(WriteError::ConnectionLost(ConnectionLost::PeerClosed {
code: NO_ERROR,
reason: b"bye".to_vec(),
})),
"CONTRACT-5b §2.5 lists `blocked_ackers`' wakers as `StreamFinished` \
and `StreamReset` and omits the latch — a parked `acked()` that the \
death does not wake is a permanent hang, and it is the one shape the \
application cannot poll its way out of"
);
assert_eq!(
within(conn_acked, "the parked Connection::acked at the death").await,
Err(ConnectionLost::PeerClosed {
code: NO_ERROR,
reason: b"bye".to_vec(),
}),
"the contract *does* say `settled_wakers` is woken on the latch — this \
half is here so a red on the half above is unambiguous"
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn s28_stream_acked_parks_before_finish_and_resolves_after_it() {
local(async {
let pair = Pair::seeded(0x5286_0506);
let (ca, cb) = pair.establish().await;
let want = payload(4096);
let mut send = within(ca.open_uni(), "open_uni").await.expect("open_uni");
write_all(&mut send, &want, "write").await;
settle().await;
let mut recv = within(cb.accept_uni(), "accept_uni")
.await
.expect("accept_uni");
assert!(
is_pending(send.acked()).await,
"ruling 139(e): before `finish()` there is no FIN to acknowledge, so \
`acked()` parks — even with every written byte already acknowledged. \
A build that resolves here tells `write().await; acked().await;` that \
a stream it has not finished is safely delivered."
);
within(send.finish(), "finish").await.expect("finish");
assert_eq!(
within(send.acked(), "acked after finish").await,
Ok(()),
"§16.2:4424: and once the FIN *is* queued and acknowledged it \
resolves — the half that stops the park above being satisfied by a \
build that never resolves at all"
);
assert_same_bytes(
&read_to_end(&mut recv, "read to end").await,
&want,
"the peer saw the data and the FIN",
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn s28_stream_acked_reports_the_reset_that_abandoned_its_bytes() {
local(async {
let pair = Pair::seeded(0x5287_0507);
let (ca, _cb) = pair.establish().await;
const CODE: u64 = 0x2a;
let lost_before = blackholed(&pair);
pair.net.block_path(pair.a.addr(), pair.b.addr());
let mut send = within(ca.open_uni(), "open_uni").await.expect("open_uni");
write_all(&mut send, &payload(4096), "write into the blackhole").await;
settle().await;
within(send.finish(), "finish").await.expect("finish");
settle().await;
assert!(
blackholed(&pair) > lost_before,
"the bytes must be genuinely unacknowledgeable, or the `Pending` below \
is a race the test happened to win"
);
assert!(
is_pending(send.acked()).await,
"nothing can be acknowledged across a blackholed path, so `acked()` \
parks — the half that makes the resolution below attributable to the \
reset"
);
send.reset(CODE);
settle().await;
assert_eq!(
within(send.acked(), "acked after reset").await,
Err(WriteError::Reset(CODE)),
"§16.2:4425 / CONTRACT-5b §2.5 outcome 1: the reset outranks \
everything, carries the application's own code, and is never \
`WriteError::Finished` — {CODE:#x} is deliberately not `0`, which is \
§16.2's drop default, so the two cannot be confused"
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn s28_connection_acked_resolves_when_a_stream_in_the_snapshot_is_reset() {
local(async {
let pair = Pair::seeded(0x5288_0508);
let (ca, _cb) = pair.establish().await;
let lost_before = blackholed(&pair);
pair.net.block_path(pair.a.addr(), pair.b.addr());
let mut s1 = within(ca.open_uni(), "open s1").await.expect("open s1");
write_all(&mut s1, &payload(4096), "s1 write").await;
let mut s2 = within(ca.open_uni(), "open s2").await.expect("open s2");
write_all(&mut s2, &payload(4096), "s2 write").await;
settle().await;
assert!(
blackholed(&pair) > lost_before,
"both streams' bytes must be unacknowledgeable for the snapshot to be \
unsettled at the call"
);
let mut snapshot = pin!(ca.acked());
assert!(
poll_once(snapshot.as_mut()).await.is_pending(),
"the snapshot holds 8 KiB that no acknowledgement can reach"
);
s1.reset(1);
s2.reset(2);
settle().await;
assert_eq!(
within(snapshot, "the held snapshot after both resets").await,
Ok(()),
"§16.2:4431: every byte in the snapshot is now abandoned by a reset, \
which settles it — waiting on an abandoned byte never terminates"
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn s28_connection_acked_terminates_while_a_bulk_stream_is_still_being_written() {
local(async {
let pair = Pair::seeded(0x5289_0509);
let (ca, cb) = pair.establish().await;
let delay = FlakyPolicy::perfect().with_delay(Duration::from_millis(5), Duration::ZERO);
pair.a.wire.set_policy(delay.clone());
pair.b.wire.set_policy(delay);
let closed_payload = payload(4096);
let mut done = within(ca.open_uni(), "open done").await.expect("open");
write_all(&mut done, &closed_payload, "done write").await;
within(done.finish(), "done finish").await.expect("finish");
let mut bulk = within(ca.open_uni(), "open bulk").await.expect("open");
write_all(&mut bulk, &payload(1024), "bulk seed").await;
settle().await;
let mut r_done = within(cb.accept_uni(), "accept done")
.await
.expect("accept");
let mut r_bulk = within(cb.accept_uni(), "accept bulk")
.await
.expect("accept");
let written = Rc::new(Cell::new(0usize));
let counter = Rc::clone(&written);
let writer = tokio::task::spawn_local(async move {
let chunk = payload(4096);
loop {
match bulk.write(&chunk).await {
Ok(n) => counter.set(counter.get() + n),
Err(_) => break,
}
tokio::task::yield_now().await;
}
});
let reader = tokio::task::spawn_local(async move {
let mut buf = vec![0u8; 8192];
while let Ok(Some(_)) = r_bulk.read(&mut buf).await {
tokio::task::yield_now().await;
}
});
settle().await;
let before = written.get();
assert!(
before > 0,
"the background writer never started; the premise of this test is that \
a bulk stream is *being written* during the call"
);
assert_eq!(
recovering(ca.acked(), "Connection::acked under a live writer").await,
Ok(()),
"§16.2:4433: the snapshot is taken once — bytes written after the call \
do not extend it. A build that re-reads every stream's offset at each \
poll never resolves this."
);
settle().await;
let after = written.get();
assert!(
after > before,
"the writer wrote {before} bytes before the call and {after} after it: \
it was not running during `acked()`, so a build that re-reads offsets \
at every poll would have terminated too and this test proved nothing"
);
writer.abort();
reader.abort();
assert_same_bytes(
&read_to_end(&mut r_done, "the finished stream").await,
&closed_payload,
"the settled half of the snapshot arrived in full",
);
})
.await;
}
const LADDER_STEP: Duration = Duration::from_millis(2);
const LADDER_WINDOW: Duration = Duration::from_secs(6);
const RUNG_TOLERANCE: Duration = Duration::from_millis(24);
const LADDER: [u32; 6] = [1, 2, 4, 8, 8, 8];
#[tokio::test(start_paused = true)]
async fn e5a_the_probe_ladder_backs_off_and_holds_at_eight_times_the_base() {
local(async {
let pair = Pair::seeded(0xE5A0_4001);
let (ca, cb) = pair.establish().await;
let delay = FlakyPolicy::perfect().with_delay(Duration::from_millis(10), Duration::ZERO);
pair.a.wire.set_policy(delay.clone());
pair.b.wire.set_policy(delay);
let mut send = within(ca.open_uni(), "open_uni").await.expect("open_uni");
write_all(&mut send, &payload(1024), "warm write").await;
let mut recv = within(cb.accept_uni(), "accept_uni")
.await
.expect("accept_uni");
assert_eq!(
within(ca.acked(), "warm acked").await,
Ok(()),
"fixture: the warm-up must be acknowledged, or §13.1 has no \
sample and the ladder below is drawn on `K_INITIAL_RTT`"
);
let mut buf = vec![0u8; 4096];
let _ = within(recv.read(&mut buf), "warm read").await;
settle().await;
let before = blackholed(&pair);
pair.net.block_path(pair.a.addr(), pair.b.addr());
write_all(&mut send, &payload(1024), "blackholed write").await;
settle().await;
assert!(
blackholed(&pair) > before,
"the blackhole destroyed nothing: `Network::sends()` and the tap \
moved together, so the write reached the fabric and no probe \
train can start"
);
let start = tokio::time::Instant::now();
let mut marks: Vec<Duration> = Vec::new();
let mut last = blackholed(&pair);
while tokio::time::Instant::now() - start < LADDER_WINDOW {
tokio::time::advance(LADDER_STEP).await;
settle().await;
let n = blackholed(&pair);
if n > last {
marks.push(tokio::time::Instant::now() - start);
last = n;
}
}
let mut rungs: Vec<Duration> = Vec::new();
let mut prev = Duration::ZERO;
for m in &marks {
rungs.push(*m - prev);
prev = *m;
}
assert!(
rungs.len() >= LADDER.len(),
"fixture: only {} rung(s) were sampled in {LADDER_WINDOW:?} — \
{rungs:?}. Fewer than {} cannot separate a capped ladder from \
an uncapped one",
rungs.len(),
LADDER.len()
);
let base = rungs[0];
assert!(
rungs.iter().all(|r| *r > Duration::ZERO),
"**ruling 223's confound.** Two departures on one instant read \
out as a 0 ns rung, and the usual cause is §7.3's path \
validation leaking into the window: B's standing \
`PATH_CHALLENGE` (ruling 208) obliges A to emit a \
`PATH_RESPONSE`, which this counter cannot tell from a probe. \
The warm-up above quiesces that exchange by advancing virtual \
time through a full acknowledged round trip — the same repair \
`s12_the_probe_train_backs_off_…` makes explicitly. If this \
fires, something A owes that is not a probe is being counted \
as one. Rungs {rungs:?}"
);
let multipliers: Vec<u32> = rungs
.iter()
.map(|r| {
u32::try_from((r.as_nanos() * 2 + base.as_nanos()) / (base.as_nanos() * 2))
.expect("a multiplier fits in u32")
})
.collect();
assert_eq!(
multipliers[..LADDER.len()],
LADDER,
"§13.3: the probe interval is `2^pto_count` capped at \
`PTO_BACKOFF_CAP` = 2³. Base {base:?}, rungs {rungs:?}"
);
assert!(
rungs[..LADDER.len()].iter().any(|r| *r != base),
"§13.3: *not all equal* — a build that never backs off announces \
the base interval for ever and probes a dead path ~13 times a \
second. Rungs {rungs:?}"
);
let ceiling = base * 8 + RUNG_TOLERANCE;
assert!(
rungs.iter().all(|r| *r <= ceiling),
"§13.3's envelope: *the probe cadence never thins beyond 8 × \
PTO*. Base {base:?}, ceiling {ceiling:?}, rungs {rungs:?} — at \
the inherited 2⁶ the later rungs could not fire inside 25 s at \
any warm RTT, which is what ruling 254 measured"
);
assert_eq!(
(multipliers[LADDER.len() - 1], multipliers[LADDER.len() - 2]),
(8, 8),
"the capped rung is **reached and held**: a ladder still \
climbing at the end of the window has not shown its ceiling, \
and a ladder that never left the base has no ceiling to show. \
Rungs {rungs:?}"
);
assert!(
blackholed(&pair) >= before + 1 + marks.len(),
"every rung above must correspond to a datagram the fabric \
destroyed; the counter says otherwise"
);
})
.await;
}
const E5B_RUNS: usize = 20;
const E5B_FLOOR: usize = 6;
const E5B_LEN: usize = 32 * 1024;
const E5B_ONE_WAY: Duration = Duration::from_millis(50);
async fn transfer_at_half_loss(seed: u64) -> (bool, usize) {
let pair = Pair::seeded(seed);
let (ca, cb) = pair.establish().await;
let policy = FlakyPolicy::lossy(0.5).with_delay(E5B_ONE_WAY, Duration::ZERO);
pair.a.wire.set_policy(policy.clone());
pair.b.wire.set_policy(policy);
let want = payload(E5B_LEN);
let Ok(Ok(mut send)) = tokio::time::timeout(DEAD_TIMEOUT, ca.open_uni()).await else {
return (false, 0);
};
let delivered = Rc::new(Cell::new(0usize));
let counter = Rc::clone(&delivered);
let writer = async {
let mut done = 0usize;
while done < want.len() {
match send.write(&want[done..]).await {
Ok(n) => done += n,
Err(_) => return false,
}
}
send.finish().await.is_ok()
};
let reader = async {
let mut recv = cb.accept_uni().await.ok()?;
let mut out = Vec::new();
let mut buf = vec![0u8; 4096];
loop {
match recv.read(&mut buf).await {
Ok(Some(n)) => {
out.extend_from_slice(&buf[..n]);
counter.set(out.len());
}
Ok(None) => return Some(out),
Err(_) => return None,
}
}
};
let done =
match tokio::time::timeout(DEAD_TIMEOUT, async { tokio::join!(writer, reader) }).await {
Ok((true, Some(got))) => got == want,
_ => false,
};
(done, delivered.get())
}
#[tokio::test(start_paused = true)]
async fn e5b_a_transfer_at_fifty_per_cent_loss_completes_in_most_runs() {
local(async {
let mut completed = 0usize;
let mut outcomes: Vec<String> = Vec::with_capacity(E5B_RUNS);
for i in 0..E5B_RUNS {
let (ok, got) = transfer_at_half_loss(0xE5B0_0000 + i as u64).await;
completed += usize::from(ok);
outcomes.push(if ok {
"ok".to_owned()
} else {
format!("{}%", got * 100 / E5B_LEN)
});
}
assert!(
completed >= E5B_FLOOR,
"§13.3's survival envelope: {completed} of {E5B_RUNS} bounded \
transfers completed inside `DEAD_TIMEOUT` at 50 % loss, and the \
Appendix B floor is {E5B_FLOOR} (≥ 30 %). Per-run outcome — \
`ok`, or the share of the payload that had reached the reader \
when the window closed: {outcomes:?}. Ruling 254 measured this \
exact shape: the 2⁶ cap *walks the probe interval past \
`DEAD_TIMEOUT`'s useful window, so the train stops probing at a \
survivable cadence exactly when survival is the question*. A row \
of high percentages is that failure — transfers stalled a few \
packets from the end, waiting out a 10 s or 20 s rung. A row of \
low ones would mean something else entirely and this bound \
would be the wrong instrument."
);
})
.await;
}