use std::{
future::poll_fn,
io::{self, Cursor, Read, Seek, SeekFrom, Write},
num::NonZeroUsize,
panic::{AssertUnwindSafe, catch_unwind},
pin::Pin,
sync::{
Arc, Mutex,
atomic::{AtomicUsize, Ordering},
mpsc,
},
task::{Context, Wake, Waker},
};
use futures::{
AsyncReadExt, AsyncSeekExt, AsyncWriteExt, StreamExt,
executor::block_on,
io::{AsyncRead, AsyncWrite},
};
use ntest::timeout;
use crate::Unblock;
#[test]
#[timeout(15000)]
fn small_buffers_read_every_byte_of_the_handle() {
let data = bytes(300);
let mut reader = Unblock::with_capacity(cap(64), Cursor::new(data.clone()));
let mut read = Vec::new();
let mut buf = [0; 7];
loop {
let len = block_on(reader.read(&mut buf)).unwrap();
if len == 0 {
break;
}
read.extend_from_slice(&buf[..len]);
}
assert_eq!(read, data);
}
#[test]
#[timeout(15000)]
fn large_buffers_read_every_byte_of_the_handle() {
let data = bytes(300);
let mut reader = Unblock::with_capacity(cap(64), Cursor::new(data.clone()));
let mut first = [0; 1024];
let len = block_on(reader.read(&mut first)).unwrap();
assert_eq!(len, 64);
let mut rest = Vec::new();
block_on(reader.read_to_end(&mut rest)).unwrap();
assert_eq!([&first[..len], &rest].concat(), data);
}
#[test]
#[timeout(15000)]
fn short_and_interrupted_reads_hand_over_every_byte() {
let data = bytes(40);
let mut reader = Unblock::new(Trickle {
inner: Cursor::new(data.clone()),
interrupt: true,
});
let mut read = Vec::new();
let mut buf = [0; 8];
loop {
let len = block_on(reader.read(&mut buf)).expect("interruptions are tried again");
if len == 0 {
break;
}
read.extend_from_slice(&buf[..len]);
}
assert_eq!(read, data);
}
#[test]
#[timeout(15000)]
fn a_read_error_reaches_the_caller_and_the_reads_after_it_go_on() {
let mut reader = Unblock::new(FailsOnce {
failed: false,
inner: Cursor::new(b"after".to_vec()),
});
let mut buf = [0; 8];
let error = block_on(reader.read(&mut buf)).expect_err("the first read fails");
assert_eq!(error.kind(), io::ErrorKind::ConnectionReset);
let len = block_on(reader.read(&mut buf)).unwrap();
assert_eq!(&buf[..len], b"after");
}
#[test]
#[timeout(15000)]
fn a_read_polled_again_with_another_buffer_fills_that_one() {
let (open, gate) = mpsc::channel();
let mut reader = Unblock::new(Gated {
gate,
inner: Cursor::new(b"hello".to_vec()),
});
let mut first = [0; 8];
let mut cx = Context::from_waker(Waker::noop());
assert!(
Pin::new(&mut reader)
.poll_read(&mut cx, &mut first)
.is_pending()
);
open.send(()).expect("the read waits for the test");
let mut second = [0; 8];
let len = block_on(poll_fn(|cx| {
Pin::new(&mut reader).poll_read(cx, &mut second)
}))
.unwrap();
assert_eq!(&second[..len], b"hello");
assert_eq!(first, [0; 8]);
}
#[test]
#[timeout(15000)]
fn a_read_given_up_on_keeps_what_it_read_for_the_next_read() {
let (open, gate) = mpsc::channel();
let mut duplex = Unblock::new(Gated {
gate,
inner: Duplex::new(b"in", Vec::new()),
});
let mut cx = Context::from_waker(Waker::noop());
assert!(
Pin::new(&mut duplex)
.poll_read(&mut cx, &mut [0; 8])
.is_pending()
);
open.send(()).expect("the read waits for the test");
drop(open);
block_on(duplex.write_all(b"out")).unwrap();
let mut buf = [0; 8];
let len = block_on(duplex.read(&mut buf)).unwrap();
assert_eq!(&buf[..len], b"in");
assert_eq!(block_on(duplex.into_inner()).inner.writer, b"out");
}
#[test]
#[timeout(15000)]
fn the_end_found_by_a_read_given_up_on_reaches_the_next_read() {
let (open, gate) = mpsc::channel();
let mut duplex = Unblock::new(Gated {
gate,
inner: Duplex::new(b"", Vec::new()),
});
let mut cx = Context::from_waker(Waker::noop());
assert!(
Pin::new(&mut duplex)
.poll_read(&mut cx, &mut [0; 8])
.is_pending()
);
open.send(()).expect("the read waits for the test");
drop(open);
block_on(duplex.write_all(b"out")).unwrap();
assert_eq!(block_on(duplex.read(&mut [0; 8])).unwrap(), 0);
assert_eq!(block_on(duplex.into_inner()).inner.writer, b"out");
}
#[test]
#[timeout(15000)]
fn the_bytes_read_ahead_outlive_a_write() {
let mut duplex = Unblock::new(Duplex::new(b"abcdef", Vec::new()));
let mut buf = [0; 2];
block_on(duplex.read_exact(&mut buf)).unwrap();
assert_eq!(&buf, b"ab");
block_on(async {
duplex.write_all(b"x").await?;
duplex.flush().await
})
.unwrap();
let mut rest = Vec::new();
block_on(duplex.read_to_end(&mut rest)).unwrap();
assert_eq!(rest, b"cdef");
assert_eq!(block_on(duplex.into_inner()).writer, b"x");
}
#[test]
#[timeout(15000)]
fn a_seek_from_the_current_position_counts_from_where_the_reads_got_to() {
let mut file = Unblock::with_capacity(cap(64), Cursor::new(bytes(256)));
block_on(async {
let mut buf = [0; 10];
file.read_exact(&mut buf).await?;
assert_eq!(file.seek(SeekFrom::Current(5)).await?, 15);
file.read_exact(&mut buf[..1]).await?;
assert_eq!(buf[0], 15);
assert_eq!(file.seek(SeekFrom::Current(-6)).await?, 10);
file.read_exact(&mut buf[..1]).await?;
assert_eq!(buf[0], 10);
io::Result::Ok(())
})
.unwrap();
}
#[test]
#[timeout(15000)]
fn a_seek_to_the_current_position_brings_the_handle_back_to_the_reads() {
let mut file = Unblock::with_capacity(cap(64), Cursor::new(bytes(256)));
block_on(file.read_exact(&mut [0; 10])).unwrap();
assert_eq!(block_on(file.seek(SeekFrom::Current(0))).unwrap(), 10);
assert_eq!(block_on(file.into_inner()).position(), 10);
}
#[test]
#[timeout(15000)]
fn a_seek_out_of_range_fails_and_keeps_the_bytes_read_ahead() {
let mut file = Unblock::with_capacity(cap(64), Cursor::new(bytes(256)));
let mut buf = [0; 10];
block_on(file.read_exact(&mut buf)).unwrap();
let error = block_on(file.seek(SeekFrom::Current(i64::MIN))).expect_err("out of range");
assert_eq!(error.kind(), io::ErrorKind::InvalidInput);
block_on(file.read_exact(&mut buf[..1])).unwrap();
assert_eq!(buf[0], 10);
}
#[test]
#[timeout(15000)]
fn a_seek_the_handle_fails_keeps_the_bytes_read_ahead() {
let mut file = Unblock::with_capacity(cap(64), Cursor::new(bytes(256)));
let mut buf = [0; 10];
block_on(file.read_exact(&mut buf)).unwrap();
block_on(file.seek(SeekFrom::Current(-1000))).expect_err("a seek to before the start fails");
block_on(file.read_exact(&mut buf[..1])).unwrap();
assert_eq!(buf[0], 10);
}
#[test]
#[timeout(15000)]
fn an_interrupted_seek_is_tried_again() {
let mut handle = Unblock::new(Hiccups {
inner: Cursor::new(bytes(10)),
..Hiccups::default()
});
assert_eq!(block_on(handle.seek(SeekFrom::Start(4))).unwrap(), 4);
let handle = block_on(handle.into_inner());
assert_eq!(handle.seeks, 2);
assert_eq!(handle.inner.position(), 4);
}
#[test]
#[timeout(15000)]
fn a_write_and_a_flush_reach_the_handle() {
let log = Arc::new(Mutex::new(Log::default()));
let mut writer = Unblock::new(Recorder(log.clone()));
block_on(async {
writer.write_all(b"hello").await?;
writer.flush().await
})
.unwrap();
let log = log.lock().unwrap();
assert_eq!(log.written, b"hello");
assert_eq!(log.flushes, 1);
}
#[test]
#[timeout(15000)]
fn an_interrupted_flush_is_tried_again() {
let mut writer = Unblock::new(Hiccups::default());
block_on(writer.flush()).unwrap();
assert_eq!(block_on(writer.into_inner()).flushes, 2);
}
#[test]
#[timeout(15000)]
fn a_write_error_reaches_the_next_write() {
let mut writer = Unblock::new(Broken);
assert_eq!(block_on(writer.write(b"lost")).unwrap(), 4);
let error = block_on(writer.write(b"again")).expect_err("the write before failed");
assert_eq!(error.kind(), io::ErrorKind::BrokenPipe);
assert_eq!(block_on(writer.write(b"again")).unwrap(), 5);
block_on(writer.flush()).expect_err("the write before failed");
}
#[test]
#[timeout(15000)]
fn a_write_error_reaches_the_next_flush() {
let mut writer = Unblock::new(Broken);
assert_eq!(block_on(writer.write(b"lost")).unwrap(), 4);
let error = block_on(writer.flush()).expect_err("the write before failed");
assert_eq!(error.kind(), io::ErrorKind::BrokenPipe);
block_on(writer.flush()).unwrap();
}
#[test]
#[timeout(15000)]
fn a_write_error_found_by_a_read_is_kept_for_the_next_write() {
let mut duplex = Unblock::new(Duplex::new(b"in", Broken));
assert_eq!(block_on(duplex.write(b"lost")).unwrap(), 4);
let mut buf = [0; 8];
let len = block_on(duplex.read(&mut buf)).expect("the read is not the write's to fail");
assert_eq!(&buf[..len], b"in");
let error = block_on(duplex.write(b"again")).expect_err("the write before failed");
assert_eq!(error.kind(), io::ErrorKind::BrokenPipe);
}
#[test]
#[timeout(15000)]
fn into_inner_hands_back_a_handle_holding_every_byte_written() {
let data = bytes(100);
let mut writer = Unblock::with_capacity(cap(4), Vec::new());
block_on(writer.write_all(&data)).unwrap();
assert_eq!(block_on(writer.into_inner()), data);
}
#[test]
#[timeout(15000)]
fn a_dropped_adapter_still_writes_what_it_took() {
let (report, reported) = mpsc::channel();
let mut writer = Unblock::new(Reporting(report));
block_on(writer.write_all(b"taken")).unwrap();
drop(writer);
assert_eq!(reported.recv().expect("the write goes on"), b"taken");
}
#[test]
#[timeout(15000)]
fn with_mut_runs_on_the_handle_and_hands_back_what_it_returned() {
let mut reader = Unblock::new(Cursor::new(bytes(10)));
let len = block_on(reader.with_mut(|cursor| {
cursor.set_position(3);
cursor.get_ref().len()
}));
assert_eq!(len, 10);
let mut buf = [0; 1];
block_on(reader.read_exact(&mut buf)).unwrap();
assert_eq!(buf[0], 3);
}
#[test]
#[timeout(15000)]
fn get_mut_reaches_the_handle_and_drops_what_was_read_ahead() {
let mut reader = Unblock::with_capacity(cap(8), Cursor::new(bytes(20)));
let mut buf = [0; 2];
block_on(reader.read_exact(&mut buf)).unwrap();
assert_eq!(block_on(reader.get_mut()).position(), 8);
block_on(reader.read_exact(&mut buf[..1])).unwrap();
assert_eq!(buf[0], 8);
}
#[test]
#[timeout(15000)]
fn a_stream_of_an_iterator_yields_every_item_in_order_and_ends() {
let mut stream = Unblock::new(0..100);
let items: Vec<_> = block_on((&mut stream).collect());
assert_eq!(items, (0..100).collect::<Vec<_>>());
assert_eq!(block_on(stream.next()), None);
}
#[test]
#[timeout(15000)]
fn a_stream_pulls_a_batch_of_items_at_a_time() {
for (capacity, batch) in [(DEFAULT, 16), (cap(1), 1), (cap(5), 5)] {
let pulled = Arc::new(AtomicUsize::new(0));
let counted = pulled.clone();
let iter = (0..100).inspect(move |_| {
counted.fetch_add(1, Ordering::Relaxed);
});
let mut stream = Unblock::with_capacity(capacity, iter);
assert_eq!(block_on(stream.next()), Some(0));
assert_eq!(pulled.load(Ordering::Relaxed), batch);
}
}
#[test]
#[timeout(15000)]
fn a_panic_in_an_operation_reaches_the_caller_and_takes_the_handle() {
let mut reader = Unblock::new(Panicking);
let panicked = catch_unwind(AssertUnwindSafe(|| block_on(reader.read(&mut [0; 8]))))
.expect_err("the read panicked");
assert_eq!(
panicked.downcast_ref::<&str>(),
Some(&"the handle panicked"),
);
for later in [
catch_unwind(AssertUnwindSafe(|| {
block_on(reader.read(&mut [0; 8])).ok();
})),
catch_unwind(AssertUnwindSafe(|| {
block_on(reader.get_mut());
})),
] {
let lost = later.expect_err("the adapter has no handle left");
assert_eq!(lost.downcast_ref::<&str>(), Some(&LOST));
}
}
#[test]
#[timeout(15000)]
fn a_panic_in_with_mut_reaches_the_caller_and_takes_the_handle() {
let mut reader = Unblock::new(Cursor::new(bytes(10)));
let panicked = catch_unwind(AssertUnwindSafe(|| {
block_on(reader.with_mut(|_| panic!("the operation panicked")))
}))
.expect_err("the operation panicked");
assert_eq!(
panicked.downcast_ref::<&str>(),
Some(&"the operation panicked"),
);
let lost = catch_unwind(AssertUnwindSafe(|| block_on(reader.into_inner())))
.expect_err("the adapter has no handle left");
assert_eq!(lost.downcast_ref::<&str>(), Some(&LOST));
}
#[test]
#[timeout(15000)]
fn two_tasks_waiting_for_one_operation_are_both_woken() {
let (open, gate) = mpsc::channel();
let mut duplex = Unblock::new(Gated {
gate,
inner: Duplex::new(b"in", Vec::new()),
});
let (report, reported) = mpsc::channel();
let reader = Waker::from(Arc::new(Report(report.clone(), "reader")));
let writer = Waker::from(Arc::new(Report(report, "writer")));
let mut buf = [0; 8];
assert!(
Pin::new(&mut duplex)
.poll_read(&mut Context::from_waker(&reader), &mut buf)
.is_pending()
);
assert!(
Pin::new(&mut duplex)
.poll_write(&mut Context::from_waker(&writer), b"out")
.is_pending()
);
open.send(()).expect("the read waits for the test");
let mut woken = [(); 2].map(|()| reported.recv().expect("both tasks are woken"));
woken.sort();
assert_eq!(woken, ["reader", "writer"]);
let written = block_on(poll_fn(|cx| Pin::new(&mut duplex).poll_write(cx, b"out"))).unwrap();
assert_eq!(written, 3);
let len = block_on(poll_fn(|cx| Pin::new(&mut duplex).poll_read(cx, &mut buf))).unwrap();
assert_eq!(&buf[..len], b"in");
assert_eq!(block_on(duplex.into_inner()).inner.writer, b"out");
}
#[test]
#[timeout(15000)]
fn a_waker_that_panics_leaves_the_other_tasks_woken() {
let (open, gate) = mpsc::channel();
let mut duplex = Unblock::new(Gated {
gate,
inner: Duplex::new(b"in", Vec::new()),
});
let (report, reported) = mpsc::channel();
let reader = Waker::from(Arc::new(Panic));
let writer = Waker::from(Arc::new(Report(report, "writer")));
let mut buf = [0; 8];
assert!(
Pin::new(&mut duplex)
.poll_read(&mut Context::from_waker(&reader), &mut buf)
.is_pending()
);
assert!(
Pin::new(&mut duplex)
.poll_write(&mut Context::from_waker(&writer), b"out")
.is_pending()
);
open.send(()).expect("the read waits for the test");
assert_eq!(reported.recv(), Ok("writer"));
let written = block_on(poll_fn(|cx| Pin::new(&mut duplex).poll_write(cx, b"out"))).unwrap();
assert_eq!(written, 3);
let len = block_on(poll_fn(|cx| Pin::new(&mut duplex).poll_read(cx, &mut buf))).unwrap();
assert_eq!(&buf[..len], b"in");
assert_eq!(block_on(duplex.into_inner()).inner.writer, b"out");
}
#[test]
#[timeout(15000)]
fn an_adapter_prints_without_its_handle() {
struct NotDebug;
assert_eq!(format!("{:?}", Unblock::new(NotDebug)), "Unblock { .. }");
}
const LOST: &str = "the handle of an `Unblock` was lost to a panic in an operation on it";
const DEFAULT: NonZeroUsize = NonZeroUsize::new(8 * 1024).unwrap();
fn bytes(len: usize) -> Vec<u8> {
(0..len).map(|i| i as u8).collect()
}
fn cap(cap: usize) -> NonZeroUsize {
NonZeroUsize::new(cap).expect("a capacity of no bytes")
}
struct Duplex<W> {
reader: Cursor<Vec<u8>>,
writer: W,
}
impl<W> Duplex<W> {
fn new(incoming: &[u8], writer: W) -> Self {
Self {
reader: Cursor::new(incoming.to_vec()),
writer,
}
}
}
impl<W> Read for Duplex<W> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.reader.read(buf)
}
}
impl<W> Write for Duplex<W>
where
W: Write,
{
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.writer.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.writer.flush()
}
}
struct Gated<T> {
gate: mpsc::Receiver<()>,
inner: T,
}
impl<T> Read for Gated<T>
where
T: Read,
{
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.gate
.recv()
.map_err(|_| io::Error::other("a read the test did not let go on"))?;
self.inner.read(buf)
}
}
impl<T> Write for Gated<T>
where
T: Write,
{
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
}
struct Trickle {
inner: Cursor<Vec<u8>>,
interrupt: bool,
}
impl Read for Trickle {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.interrupt = !self.interrupt;
if !self.interrupt {
return Err(io::ErrorKind::Interrupted.into());
}
let len = buf.len().min(1);
self.inner.read(&mut buf[..len])
}
}
struct FailsOnce {
failed: bool,
inner: Cursor<Vec<u8>>,
}
impl Read for FailsOnce {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if !self.failed {
self.failed = true;
return Err(io::ErrorKind::ConnectionReset.into());
}
self.inner.read(buf)
}
}
struct Panicking;
impl Read for Panicking {
fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
panic!("the handle panicked");
}
}
#[derive(Default)]
struct Log {
written: Vec<u8>,
flushes: usize,
}
struct Recorder(Arc<Mutex<Log>>);
impl Write for Recorder {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.lock().unwrap().written.extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
self.0.lock().unwrap().flushes += 1;
Ok(())
}
}
struct Reporting(mpsc::Sender<Vec<u8>>);
impl Write for Reporting {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.send(buf.to_vec()).ok();
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
struct Broken;
impl Write for Broken {
fn write(&mut self, _: &[u8]) -> io::Result<usize> {
Err(io::ErrorKind::BrokenPipe.into())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[derive(Default)]
struct Hiccups {
inner: Cursor<Vec<u8>>,
flushes: usize,
seeks: usize,
}
impl Write for Hiccups {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.flushes += 1;
if self.flushes == 1 {
return Err(io::ErrorKind::Interrupted.into());
}
Ok(())
}
}
impl Seek for Hiccups {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
self.seeks += 1;
if self.seeks == 1 {
return Err(io::ErrorKind::Interrupted.into());
}
self.inner.seek(pos)
}
}
struct Report(mpsc::Sender<&'static str>, &'static str);
impl Wake for Report {
fn wake(self: Arc<Self>) {
self.0.send(self.1).ok();
}
}
struct Panic;
impl Wake for Panic {
fn wake(self: Arc<Self>) {
panic!("the waker panicked");
}
}