#![allow(clippy::items_after_statements)]
use std::future::Future;
use std::pin::pin;
use std::task::Poll;
use std::time::Duration;
use slither::constants::{INITIAL_MAX_DATA, INITIAL_MAX_STREAM_DATA, INITIAL_MAX_STREAMS_UNI};
use slither::error::{ConnectionLost, ReadError, WriteError};
use slither::testutil::{Pair, TestBiStream, TestRecvStream, TestSendStream, local, settle};
const PATIENCE: Duration = Duration::from_secs(5);
const NOT_BEFORE: Duration = Duration::from_millis(200);
async fn within<F: Future>(fut: F, what: &str) -> F::Output {
match tokio::time::timeout(PATIENCE, fut).await {
Ok(v) => v,
Err(_) => panic!("{what}: still pending after {PATIENCE:?} of virtual time"),
}
}
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");
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]
);
}
}
async fn write_all(s: &mut TestSendStream, buf: &[u8], what: &str) {
let mut done = 0usize;
while done < buf.len() {
let n = within(s.write(&buf[done..]), what)
.await
.unwrap_or_else(|e| panic!("{what}: write failed with {e:?}"));
assert!(n >= 1, "{what}: `Ok(0)` means only that `buf` was empty");
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 within(r.read(&mut buf), what).await {
Ok(Some(n)) => {
assert!(
n >= 1,
"{what}: `Ok(Some(0))` means only that `buf` was empty"
);
out.extend_from_slice(&buf[..n]);
}
Ok(None) => break,
Err(e) => panic!("{what}: expected a clean end of stream, got {e:?}"),
}
}
out
}
async fn drain_exactly(r: &mut TestRecvStream, n: usize, what: &str) {
let mut done = 0usize;
while done < n {
let want = (n - done).min(8192);
let mut buf = vec![0u8; want];
match within(r.read(&mut buf), what).await {
Ok(Some(k)) => done += k,
other => panic!("{what}: wanted {n} bytes, got {other:?} after {done}"),
}
}
}
async fn fill_until_blocked(s: &mut TestSendStream, buf: &[u8], what: &str) -> (usize, bool) {
let mut done = 0usize;
loop {
if done == buf.len() {
return (done, false);
}
match tokio::time::timeout(NOT_BEFORE, s.write(&buf[done..])).await {
Ok(Ok(n)) => {
assert!(
n >= 1,
"{what}: a blocked write is `Pending`, never `Ok(0)`"
);
done += n;
}
Ok(Err(e)) => panic!("{what}: write failed with {e:?}"),
Err(_) => return (done, true),
}
}
}
async fn read_until_end(r: &mut TestRecvStream, what: &str) -> Result<usize, ReadError> {
let mut total = 0usize;
loop {
match within(r.read(&mut [0u8; 2048]), what).await {
Ok(Some(n)) => total += n,
Ok(None) => return Ok(total),
Err(e) => return Err(e),
}
}
}
#[tokio::test(start_paused = true)]
async fn dropping_an_unfinished_send_stream_resets_it_with_code_zero() {
local(async {
let pair = Pair::seeded(0x4B00_0001);
let (ca, cb) = pair.establish().await;
let mut s = within(ca.open_uni(), "open").await.expect("open");
write_all(&mut s, b"half a message", "probe").await;
settle().await;
let mut r = within(cb.accept_uni(), "accept").await.expect("accept");
drop(s);
settle().await;
match read_until_end(&mut r, "peer read").await {
Err(e) => assert_eq!(
e,
ReadError::Reset(0),
"§16.2: the drop of an unfinished `SendStream` is a reset with \
code 0 — exactly 0, because that is what distinguishes it from \
an application's own code"
),
Ok(_) => panic!(
"§16.2: dropping a `SendStream` **without** `finish()` resets it; \
a `Drop` that does nothing ends the stream cleanly instead"
),
}
})
.await;
}
#[tokio::test(start_paused = true)]
async fn dropping_a_finished_send_stream_still_delivers_the_end_of_stream() {
local(async {
let pair = Pair::seeded(0x4B00_0002);
let (ca, cb) = pair.establish().await;
let want = payload(40 * 1024);
let mut s = within(ca.open_uni(), "open").await.expect("open");
write_all(&mut s, &want, "bulk").await;
let mut r = within(cb.accept_uni(), "accept").await.expect("accept");
within(s.finish(), "finish").await.expect("finish");
settle().await;
drop(s);
settle().await;
let got = read_to_end(&mut r, "peer read after the sender dropped").await;
assert_same_bytes(&got, &want, "a finished-then-dropped stream's contents");
})
.await;
}
#[tokio::test(start_paused = true)]
async fn a_reset_after_a_delivered_fin_is_a_no_op() {
local(async {
let pair = Pair::seeded(0x4B00_0003);
let (ca, cb) = pair.establish().await;
let want = payload(8 * 1024);
let mut s = within(ca.open_uni(), "open").await.expect("open");
write_all(&mut s, &want, "bulk").await;
let mut r = within(cb.accept_uni(), "accept").await.expect("accept");
within(s.finish(), "finish").await.expect("finish");
settle().await;
s.reset(0x77);
settle().await;
assert_same_bytes(
&read_to_end(&mut r, "peer read after a reset that followed the FIN").await,
&want,
"§9.6: a RESET_STREAM for an already-FIN-complete receive half is a \
valid no-op when the final sizes agree",
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn dropping_a_send_stream_that_parked_on_credit_still_resets_it() {
local(async {
let pair = Pair::seeded(0x4B00_0004);
let (ca, cb) = pair.establish().await;
let window = INITIAL_MAX_STREAM_DATA as usize;
let over = payload(window + 4096);
let mut s = within(ca.open_uni(), "open").await.expect("open");
let (accepted, blocked) = fill_until_blocked(&mut s, &over, "fill").await;
assert!(blocked, "§10.1: the reader has read nothing");
assert_eq!(accepted, window, "§10.2: parked at the stream window");
let mut r = within(cb.accept_uni(), "accept").await.expect("accept");
drop(s);
settle().await;
match read_until_end(&mut r, "peer read").await {
Err(e) => assert_eq!(e, ReadError::Reset(0)),
Ok(_) => panic!(
"§16.2: a `SendStream` dropped while it had a waker registered is \
still a `SendStream` dropped without `finish()`"
),
}
})
.await;
}
#[tokio::test(start_paused = true)]
async fn dropping_a_recv_stream_stalls_only_that_stream() {
local(async {
let pair = Pair::seeded(0x4B00_0005);
let (ca, cb) = pair.establish().await;
let window = INITIAL_MAX_STREAM_DATA as usize;
let over = payload(window + 8192);
let mut s = within(ca.open_uni(), "open abandoned").await.expect("open");
write_all(&mut s, &payload(2048), "first bytes").await;
settle().await;
let r = within(cb.accept_uni(), "accept").await.expect("accept");
drop(r);
settle().await;
let (accepted, blocked) = fill_until_blocked(&mut s, &over, "fill an abandoned half").await;
assert!(
blocked,
"ruling 93: stream-level credit is never again advanced for an \
abandoned half, so the sender must stall"
);
assert_eq!(
accepted + 2048,
window,
"§10.2: it stalls at the stream window and nowhere else"
);
let sibling_bytes = payload(6000);
let mut sib = within(ca.open_uni(), "open sibling").await.expect("open");
write_all(&mut sib, &sibling_bytes, "sibling").await;
within(sib.finish(), "sibling finish")
.await
.expect("finish");
settle().await;
let mut rs = within(cb.accept_uni(), "accept sibling")
.await
.expect("accept");
assert_same_bytes(
&read_to_end(&mut rs, "sibling read").await,
&sibling_bytes,
"a sibling stream after a half was abandoned",
);
let mut closed = pin!(ca.closed());
assert!(
poll_once(closed.as_mut()).await.is_pending(),
"ruling 93: an abandoned stream never wedges the connection"
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn dropping_recv_streams_releases_the_connection_window() {
local(async {
let pair = Pair::seeded(0x4B00_0006);
let (ca, cb) = pair.establish().await;
let window = INITIAL_MAX_STREAM_DATA as usize;
let over = payload(window + 4096);
let fillers = INITIAL_MAX_DATA as usize / window;
let mut held = Vec::new();
let mut total = 0usize;
for i in 0..fillers {
let bi = within(ca.open_bi(), "open_bi filler")
.await
.expect("open_bi");
let (mut send, recv) = bi.split();
let (got, blocked) = fill_until_blocked(&mut send, &over, "filler fill").await;
assert!(blocked, "filler {i} must park at a window");
total += got;
held.push((send, recv));
}
assert_eq!(
total, INITIAL_MAX_DATA as usize,
"§10.1: four stream windows exactly exhaust the connection window"
);
let extra = within(ca.open_bi(), "open_bi extra")
.await
.expect("open_bi");
let (mut extra_send, _extra_recv) = extra.split();
settle().await;
assert!(
is_pending(extra_send.write(b"one byte")).await,
"§10.1: the connection window is spent, so even a brand-new stream parks"
);
let mut accepted = Vec::new();
for i in 0..fillers {
accepted.push(
within(cb.accept_bi(), "accept_bi")
.await
.unwrap_or_else(|e| panic!("accept_bi {i}: {e:?}")),
);
}
drop(accepted);
settle().await;
let n = within(extra_send.write(b"one byte"), "write after the abandonment")
.await
.expect("write");
assert!(
n >= 1,
"§10.3: retirement advances connection credit — without the true-up \
the connection is in a permanent send stall with no error and no timer"
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn dropping_the_recv_half_of_a_bi_stream_leaves_the_send_half_alive() {
local(async {
let pair = Pair::seeded(0x4B00_0007);
let (ca, cb) = pair.establish().await;
let want = payload(7000);
let bi = within(ca.open_bi(), "open_bi").await.expect("open_bi");
let id = bi.id();
let (mut send, recv) = bi.split();
drop(recv);
settle().await;
write_all(
&mut send,
&want,
"send half after the recv half was dropped",
)
.await;
within(send.finish(), "finish").await.expect("finish");
settle().await;
let peer = within(cb.accept_bi(), "accept_bi")
.await
.expect("accept_bi");
assert_eq!(peer.id(), id, "one stream, one id, across both ends");
let (_peer_send, mut peer_recv) = peer.split();
assert_same_bytes(
&read_to_end(&mut peer_recv, "peer read").await,
&want,
"the send half survives its sibling's drop",
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn a_live_send_stream_stops_the_last_connection_drop_from_closing_underneath_it() {
local(async {
let pair = Pair::seeded(0x4B00_0013);
let (ca, cb) = pair.establish().await;
let want = payload(5000);
let mut s = within(ca.open_uni(), "open").await.expect("open");
drop(ca);
settle().await;
let mut peer_closed = pin!(cb.closed());
assert!(
poll_once(peer_closed.as_mut()).await.is_pending(),
"ruling 115: the last `Connection` handle went, but a `SendStream` \
is a handle too — so no `close(NO_ERROR, \"\")` was performed"
);
write_all(
&mut s,
&want,
"write after the Connection handle was dropped",
)
.await;
within(s.finish(), "finish").await.expect("finish");
settle().await;
let mut r = within(cb.accept_uni(), "accept").await.expect("accept");
assert_same_bytes(
&read_to_end(&mut r, "peer read").await,
&want,
"a stream outliving its `Connection` handle still delivers",
);
drop(s);
settle().await;
assert!(
matches!(
poll_once(pin!(cb.closed()).as_mut()).await,
Poll::Ready(ConnectionLost::PeerClosed { code: 0, .. })
),
"ruling 125: the last handle to a connection can be a stream, and \
dropping it closes"
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn a_receiver_can_drain_a_stream_the_sender_closed_behind() {
local(async {
let pair = Pair::seeded(0x4B00_0014);
let (ca, cb) = pair.establish().await;
let want = payload(5000);
let mut s = within(ca.open_uni(), "open").await.expect("open");
write_all(&mut s, &want, "write").await;
within(s.finish(), "finish").await.expect("finish");
settle().await;
drop(s);
drop(ca);
settle().await;
let mut r = within(cb.accept_uni(), "accept")
.await
.expect("ruling 128: a fully-arrived stream survives the peer's close");
assert_same_bytes(
&read_to_end(&mut r, "peer read").await,
&want,
"ruling 128: every byte that arrived before the CLOSE is readable",
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn an_empty_buffer_is_the_only_meaning_of_ok_zero_on_write() {
local(async {
let pair = Pair::seeded(0x4B00_0008);
let (ca, _cb) = pair.establish().await;
let mut s = within(ca.open_uni(), "open").await.expect("open");
assert_eq!(
within(s.write(&[]), "empty write").await,
Ok(0),
"ruling 110: an empty buffer is `Ok(0)`, resolved before the core is touched"
);
assert_eq!(
within(s.write(b"x"), "one-byte write").await,
Ok(1),
"the boundary's other side: one byte is one byte"
);
let window = INITIAL_MAX_STREAM_DATA as usize;
let over = payload(window);
let (_, blocked) = fill_until_blocked(&mut s, &over, "fill").await;
assert!(blocked, "§10.1: nothing is being read");
assert!(
is_pending(s.write(b"y")).await,
"CONTRACT-4b §3: a blocked write is `Pending`. A build that returned \
`Ok(0)` here would collide with the empty-buffer row above and a \
caller could not tell the two apart"
);
assert_eq!(
within(s.write(&[]), "empty write while blocked").await,
Ok(0),
"ruling 110: the empty-buffer short-circuit runs before the credit \
check, so it answers even on a parked stream"
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn an_empty_buffer_is_the_only_meaning_of_ok_some_zero_on_read() {
local(async {
let pair = Pair::seeded(0x4B00_0009);
let (ca, cb) = pair.establish().await;
let first = payload(3000);
let second = payload(2000);
let mut s = within(ca.open_uni(), "open").await.expect("open");
write_all(&mut s, &first, "first").await;
settle().await;
let mut r = within(cb.accept_uni(), "accept").await.expect("accept");
assert_eq!(
within(r.read(&mut []), "empty read with data waiting").await,
Ok(Some(0)),
"ruling 119: an empty buffer is `Ok(Some(0))` — and it is *not* \
`Ok(None)`, even though this stream has data and no FIN"
);
let mut one = [0u8; 1];
assert_eq!(
within(r.read(&mut one), "one-byte read").await,
Ok(Some(1)),
"the boundary's other side"
);
assert_eq!(
one[0], first[0],
"the one-byte read delivered the first byte"
);
drain_exactly(&mut r, first.len() - 1, "drain the rest of the first write").await;
assert!(
is_pending(r.read(&mut [0u8; 512])).await,
"CONTRACT-4b §5: no data available is `Pending`. `Ok(Some(0))` here \
spins a caller; `Ok(None)` here reports a partial transfer as complete"
);
write_all(&mut s, &second, "second").await;
within(s.finish(), "finish").await.expect("finish");
settle().await;
let rest = read_to_end(&mut r, "drain second").await;
assert_same_bytes(
&rest,
&second,
"the bytes that follow a quiet moment — proof the quiet moment was \
not the end of the stream",
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn finish_resolves_immediately_is_idempotent_and_closes_the_write_verbs() {
local(async {
let pair = Pair::seeded(0x4B00_000A);
let (ca, _cb) = pair.establish().await;
let mut s = within(ca.open_uni(), "open").await.expect("open");
write_all(&mut s, b"a message", "probe").await;
{
let mut fin = pin!(s.finish());
match poll_once(fin.as_mut()).await {
Poll::Ready(Ok(())) => {}
other => panic!(
"§16.2: `finish()` resolves on the **first** poll — it accepts \
the FIN into send state and does not wait for the peer. \
Got {other:?}"
),
}
}
assert_eq!(
within(s.finish(), "second finish").await,
Ok(()),
"CONTRACT-4b §3: `finish()` is idempotent"
);
assert_eq!(
within(s.write(b"after"), "write after finish").await,
Err(WriteError::Finished),
"§16.2: a finished stream accepts no more bytes"
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn reset_is_idempotent_and_the_first_code_wins() {
local(async {
let pair = Pair::seeded(0x4B00_000B);
let (ca, cb) = pair.establish().await;
let mut s = within(ca.open_uni(), "open").await.expect("open");
write_all(&mut s, &payload(1500), "probe").await;
settle().await;
let mut r = within(cb.accept_uni(), "accept").await.expect("accept");
s.reset(0x11);
s.reset(0x22);
settle().await;
match read_until_end(&mut r, "peer read").await {
Err(e) => assert_eq!(
e,
ReadError::Reset(0x11),
"CONTRACT-4b §3: idempotent, first code wins"
),
Ok(_) => panic!("§9.6: a reset stream does not end cleanly"),
}
assert_eq!(
within(s.write(b"more"), "write after reset").await,
Err(WriteError::Finished),
"CONTRACT-4b §3: a locally reset half reports `Finished`, never \
`Reset` — `WriteError::Reset` has no producer in slice 4"
);
assert_eq!(
within(s.finish(), "finish after reset").await,
Err(WriteError::Finished)
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn id_keeps_answering_after_the_stream_closes_and_after_the_connection_dies() {
local(async {
let pair = Pair::seeded(0x4B00_000C);
let (ca, cb) = pair.establish().await;
let want = payload(2500);
let mut s = within(ca.open_uni(), "open").await.expect("open");
let sid = s.id().expect("ruling 116: a held handle answers `Some`");
write_all(&mut s, &want, "probe").await;
within(s.finish(), "finish").await.expect("finish");
settle().await;
let mut r = within(cb.accept_uni(), "accept").await.expect("accept");
let rid = r.id().expect("ruling 116");
assert_eq!(rid, sid, "both ends name one stream");
assert_same_bytes(&read_to_end(&mut r, "read").await, &want, "contents");
assert_eq!(
r.id(),
Some(rid),
"ruling 116: `id()` keeps answering after the stream is fully \
closed — an uncached build answers `None` from here on"
);
ca.close(0, b"").await;
cb.close(0, b"").await;
settle().await;
assert_eq!(
s.id(),
Some(sid),
"CONTRACT-4b §8: `id` keeps answering, like `remote_static()`"
);
assert_eq!(r.id(), Some(rid));
})
.await;
}
#[tokio::test(start_paused = true)]
async fn join_rejects_mismatched_halves_and_hands_them_back_unchanged() {
local(async {
let pair = Pair::seeded(0x4B00_000D);
let (ca, cb) = pair.establish().await;
let bi1 = within(ca.open_bi(), "open_bi 1").await.expect("open_bi");
let bi2 = within(ca.open_bi(), "open_bi 2").await.expect("open_bi");
let id1 = bi1.id().expect("ruling 116");
let id2 = bi2.id().expect("ruling 116");
assert_ne!(id1, id2);
let (s1, r1) = bi1.split();
let (s2, r2) = bi2.split();
let (s1, r2) = match TestBiStream::join(s1, r2) {
Ok(_) => panic!(
"ruling 120: `join` validates same-`StreamRef`-same-`ConnectionId`; \
accepting a mismatch builds a handle whose `Drop` resets a stream \
the caller never named"
),
Err(pair) => pair,
};
assert_eq!(
s1.id(),
Some(id1),
"ruling 120: the halves are handed back **unchanged**"
);
assert_eq!(r2.id(), Some(id2));
let want = payload(1200);
let rejoined = match TestBiStream::join(s1, r1) {
Ok(bi) => bi,
Err(_) => panic!("ruling 120: the matching pair must join"),
};
assert_eq!(rejoined.id(), Some(id1), "`join` preserves the id");
let (mut send, _recv) = rejoined.split();
write_all(&mut send, &want, "after a rejected join").await;
within(send.finish(), "finish").await.expect("finish");
settle().await;
let peer = within(cb.accept_bi(), "accept_bi")
.await
.expect("accept_bi");
assert_eq!(peer.id(), Some(id1));
let (_ps, mut pr) = peer.split();
assert_same_bytes(&read_to_end(&mut pr, "peer read").await, &want, "contents");
drop((s2, r2));
})
.await;
}
#[tokio::test(start_paused = true)]
async fn every_stream_verb_answers_connection_lost_after_the_connection_dies() {
local(async {
let pair = Pair::seeded(0x4B00_000E);
let (ca, cb) = pair.establish().await;
let mut s = within(ca.open_uni(), "open").await.expect("open");
write_all(&mut s, &payload(1000), "probe").await;
let sid = s.id().expect("ruling 116");
settle().await;
let mut r = within(cb.accept_uni(), "accept").await.expect("accept");
let mut peer_opened = within(cb.open_uni(), "peer open").await.expect("open");
write_all(&mut peer_opened, b"never accepted", "peer probe").await;
settle().await;
ca.close(0x00, b"").await;
settle().await;
assert!(
matches!(
within(s.write(b"more"), "write after close").await,
Err(WriteError::ConnectionLost(_))
),
"CONTRACT-4b §8: `write` answers from the latch"
);
assert!(matches!(
within(s.finish(), "finish after close").await,
Err(WriteError::ConnectionLost(_))
));
s.reset(9);
assert_eq!(
s.id(),
Some(sid),
"CONTRACT-4b §8: `id` keeps answering after the connection dies"
);
let mut acc = pin!(ca.accept_uni());
match poll_once(acc.as_mut()).await {
Poll::Ready(Ok(_)) => {}
other => panic!(
"ruling 128: a stream that arrived before the death survives \
it, whoever closed. Got {}",
match other {
Poll::Pending => "Pending",
Poll::Ready(Err(_)) => "Err(ConnectionLost)",
Poll::Ready(Ok(_)) => unreachable!(),
}
),
}
let mut drained = pin!(ca.accept_uni());
assert!(
matches!(poll_once(drained.as_mut()).await, Poll::Ready(Err(_))),
"ruling 128: once nothing is left, `accept_*` reports the death — \
and never `Pending`, which would hang a claimer for ever"
);
let mut opn = pin!(ca.open_uni());
assert!(
matches!(poll_once(opn.as_mut()).await, Poll::Ready(Err(_))),
"ruling 118: `open_*` answers immediately too"
);
let _ = within(cb.closed(), "peer closed").await;
let mut got = 0usize;
loop {
let mut buf = [0u8; 256];
match within(r.read(&mut buf), "read after close").await {
Ok(Some(n)) => got += n,
Err(ReadError::ConnectionLost(_)) => break,
other => panic!("ruling 128: expected a drain then the death, got {other:?}"),
}
}
assert_eq!(
got, 1000,
"ruling 128: every byte that arrived before the CLOSE is readable \
after it — a build that drops them reports 0 here and still \
ends with `ConnectionLost`, which is why the count is asserted"
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn a_cancelled_read_loses_no_byte_and_duplicates_none() {
local(async {
let pair = Pair::seeded(0x4B00_000F);
let (ca, cb) = pair.establish().await;
let first = payload(2048);
let second = payload(3072);
let mut s = within(ca.open_uni(), "open").await.expect("open");
write_all(&mut s, &first, "first").await;
settle().await;
let mut r = within(cb.accept_uni(), "accept").await.expect("accept");
drain_exactly(&mut r, first.len(), "drain first").await;
{
let mut buf = [0u8; 4096];
let mut park = pin!(r.read(&mut buf));
assert!(
poll_once(park.as_mut()).await.is_pending(),
"nothing has been written since the drain"
);
write_all(&mut s, &second, "second").await;
within(s.finish(), "finish").await.expect("finish");
settle().await;
}
let rest = read_to_end(&mut r, "after the cancelled read").await;
assert_same_bytes(
&rest,
&second,
"every byte written after a cancelled read arrives exactly once",
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn a_cancelled_write_claims_nothing() {
local(async {
let pair = Pair::seeded(0x4B00_0010);
let (ca, cb) = pair.establish().await;
let window = INITIAL_MAX_STREAM_DATA as usize;
let half = window / 2;
let bulk = payload(window);
let mut s = within(ca.open_uni(), "open").await.expect("open");
let (accepted, _) = fill_until_blocked(&mut s, &bulk, "fill").await;
assert_eq!(
accepted, window,
"§10.2: the window admits exactly this much"
);
let ghost = vec![0xFFu8; 8192];
assert!(
is_pending(s.write(&ghost)).await,
"§10.1: no credit, so this write parks and is then dropped"
);
let mut r = within(cb.accept_uni(), "accept").await.expect("accept");
drain_exactly(&mut r, half, "drain to the re-grant threshold").await;
settle().await;
let tail = vec![0xCCu8; 1024];
write_all(&mut s, &tail, "tail after the cancelled write").await;
within(s.finish(), "finish").await.expect("finish");
settle().await;
drop(s);
settle().await;
let mut want = Vec::with_capacity(window + tail.len());
want.extend_from_slice(&bulk);
want.extend_from_slice(&tail);
let mut got = want[..half].to_vec();
got.extend_from_slice(&read_to_end(&mut r, "drain the rest").await);
assert_same_bytes(
&got,
&want,
"a dropped `write` future claimed nothing — a single 0xFF byte here \
is a stored partial buffer",
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn open_uni_parks_at_the_cumulative_limit_and_resumes_when_the_peer_frees_streams() {
local(async {
let pair = Pair::seeded(0x4B00_0011);
let (ca, cb) = pair.establish().await;
let limit = INITIAL_MAX_STREAMS_UNI as usize;
for i in 0..limit {
let mut s = within(ca.open_uni(), "open within the limit")
.await
.unwrap_or_else(|e| panic!("open {i} of {limit} failed: {e:?}"));
assert_eq!(
s.id().expect("ruling 116").index(),
i as u64,
"§9.1: indices are allocated in order, one per open"
);
write_all(&mut s, b"x", "probe").await;
within(s.finish(), "finish").await.expect("finish");
drop(s);
}
settle().await;
assert!(
is_pending(ca.open_uni()).await,
"§10.4: the {limit}th stream exhausts the cumulative limit, so the \
next open parks — it does not error"
);
for i in 0..limit {
let mut r = within(cb.accept_uni(), "accept")
.await
.unwrap_or_else(|e| panic!("accept {i}: {e:?}"));
assert_eq!(
read_to_end(&mut r, "read to EOF").await,
b"x",
"a finished-then-dropped stream ends cleanly, {i} of {limit}"
);
drop(r);
}
settle().await;
let resumed = within(ca.open_uni(), "open after replenishment")
.await
.expect("§10.4: MAX_STREAMS_UNI grew, so the space reopened");
assert_eq!(
resumed.id().expect("ruling 116").index(),
limit as u64,
"§4.4: the cancelled `open_uni` above claimed nothing — a future \
dropped after `core.open()` succeeded would have spent index \
{limit} and left this one at {}",
limit + 1
);
})
.await;
}
#[tokio::test(start_paused = true)]
async fn a_cancelled_accept_claims_nothing() {
local(async {
let pair = Pair::seeded(0x4B00_0012);
let (ca, cb) = pair.establish().await;
assert!(
is_pending(cb.accept_uni()).await,
"no stream has been opened, so `accept_uni` parks"
);
let mut s1 = within(ca.open_uni(), "open 1").await.expect("open");
write_all(&mut s1, b"one", "probe 1").await;
let mut s2 = within(ca.open_uni(), "open 2").await.expect("open");
write_all(&mut s2, b"two", "probe 2").await;
within(s1.finish(), "finish 1").await.expect("finish");
within(s2.finish(), "finish 2").await.expect("finish");
settle().await;
let mut r1 = within(cb.accept_uni(), "accept 1").await.expect("accept 1");
let mut r2 = within(cb.accept_uni(), "accept 2").await.expect("accept 2");
assert_eq!(r1.id(), s1.id(), "ruling 112: FIFO by open order");
assert_eq!(r2.id(), s2.id());
assert_eq!(read_to_end(&mut r1, "read 1").await, b"one");
assert_eq!(read_to_end(&mut r2, "read 2").await, b"two");
})
.await;
}