#![allow(
clippy::unwrap_used,
clippy::panic,
reason = "a test harness reports by panicking"
)]
use alloc::boxed::Box;
use super::{Buffer, DynBuffer, Reader};
use crate::DbError;
fn reader<B: Buffer<i32>>(buffer: &B) -> Reader<i32> {
Reader::new(Box::new(buffer.subscribe()))
}
pub async fn assert_single_latest_contract<B, F>(mk: F)
where
B: Buffer<i32> + DynBuffer<i32>,
F: Fn() -> B,
{
let buffer = mk();
Buffer::push(&buffer, 42);
let mut r = reader(&buffer);
assert_eq!(
r.recv().await.unwrap(),
42,
"fresh SingleLatest subscriber must observe the current value once"
);
assert!(
matches!(r.try_recv(), Err(DbError::BufferEmpty)),
"no new push after the first delivery must read empty, not redeliver"
);
let buffer = mk();
let mut r = reader(&buffer);
assert!(
matches!(r.try_recv(), Err(DbError::BufferEmpty)),
"nothing produced yet must read empty, not a spurious value or closure"
);
Buffer::push(&buffer, 7);
assert_eq!(r.recv().await.unwrap(), 7);
let buffer = mk();
let mut r = reader(&buffer);
Buffer::push(&buffer, 1);
Buffer::push(&buffer, 2);
Buffer::push(&buffer, 3);
assert_eq!(
r.recv().await.unwrap(),
3,
"SingleLatest keeps only the newest value"
);
let buffer = mk();
assert_eq!(
DynBuffer::peek(&buffer),
None,
"peek before any push must be None"
);
Buffer::push(&buffer, 10);
Buffer::push(&buffer, 20);
assert_eq!(DynBuffer::peek(&buffer), Some(20));
assert_eq!(
DynBuffer::peek(&buffer),
Some(20),
"peek must be non-destructive"
);
}
pub async fn assert_mailbox_contract<B, F>(mk: F)
where
B: Buffer<i32> + DynBuffer<i32>,
F: Fn() -> B,
{
let buffer = mk();
let mut r = reader(&buffer);
Buffer::push(&buffer, 1);
Buffer::push(&buffer, 2);
Buffer::push(&buffer, 3);
assert_eq!(
r.recv().await.unwrap(),
3,
"Mailbox keeps only the latest unconsumed value"
);
assert!(
matches!(r.try_recv(), Err(DbError::BufferEmpty)),
"the slot must be drained once received"
);
let buffer = mk();
let mut r = reader(&buffer);
assert!(matches!(r.try_recv(), Err(DbError::BufferEmpty)));
let buffer = mk();
assert_eq!(DynBuffer::peek(&buffer), None);
Buffer::push(&buffer, 7);
assert_eq!(DynBuffer::peek(&buffer), Some(7));
assert_eq!(
DynBuffer::peek(&buffer),
Some(7),
"peek must not drain the mailbox slot"
);
}
pub async fn assert_spmc_ring_contract<B, F>(mk: F)
where
B: Buffer<i32> + DynBuffer<i32>,
F: Fn() -> B,
{
const LAG_PUSHES: i32 = 64;
let buffer = mk();
let mut a = reader(&buffer);
let mut b = reader(&buffer);
Buffer::push(&buffer, 1);
Buffer::push(&buffer, 2);
assert_eq!(a.recv().await.unwrap(), 1);
assert_eq!(b.recv().await.unwrap(), 1);
assert_eq!(a.recv().await.unwrap(), 2);
assert_eq!(b.recv().await.unwrap(), 2);
assert!(
matches!(a.try_recv(), Err(DbError::BufferEmpty)),
"a caught-up reader reads empty"
);
let buffer = mk();
let mut r = reader(&buffer);
for i in 0..LAG_PUSHES {
Buffer::push(&buffer, i);
}
assert!(
matches!(r.recv().await, Err(DbError::BufferLagged { .. })),
"a slow reader on an overflowed ring must surface BufferLagged"
);
assert_eq!(
DynBuffer::peek(&buffer),
None,
"SpmcRing peek must be None (no canonical latest)"
);
}