Skip to content

Commit 4025e79

Browse files
Nemo157cramertj
authored andcommitted
Add Stream and AsyncRead pending test adaptors
1 parent aaa1efc commit 4025e79

File tree

7 files changed

+259
-2
lines changed

7 files changed

+259
-2
lines changed

futures-test/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ name = "futures_test"
1616

1717
[dependencies]
1818
futures-core-preview = { version = "=0.3.0-alpha.15", path = "../futures-core", default-features = false }
19+
futures-io-preview = { version = "=0.3.0-alpha.15", path = "../futures-io", default-features = false }
1920
futures-util-preview = { version = "=0.3.0-alpha.15", path = "../futures-util", default-features = false }
2021
futures-executor-preview = { version = "=0.3.0-alpha.15", path = "../futures-executor", default-features = false }
2122
pin-utils = { version = "0.1.0-alpha.4", default-features = false }
@@ -25,4 +26,4 @@ futures-preview = { version = "=0.3.0-alpha.15", path = "../futures", default-fe
2526

2627
[features]
2728
default = ["std"]
28-
std = ["futures-core-preview/std", "futures-util-preview/std", "futures-executor-preview/std"]
29+
std = ["futures-core-preview/std", "futures-io-preview/std", "futures-util-preview/std", "futures-executor-preview/std"]

futures-test/src/future/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ pub trait FutureTestExt: Future {
5252
where
5353
Self: Sized,
5454
{
55-
pending_once::PendingOnce::new(self)
55+
PendingOnce::new(self)
5656
}
5757

5858
/// Runs this future on a dedicated executor running in a background thread.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
use futures_io::{self as io, AsyncBufRead, AsyncRead};
2+
use pin_utils::{unsafe_pinned, unsafe_unpinned};
3+
use std::{
4+
marker::Unpin,
5+
pin::Pin,
6+
task::{Context, Poll},
7+
};
8+
9+
/// Reader for the [`interleave_pending`](super::AsyncReadTestExt::interleave_pending) method.
10+
#[derive(Debug)]
11+
pub struct InterleavePending<R: AsyncRead> {
12+
reader: R,
13+
pended: bool,
14+
}
15+
16+
impl<R: AsyncRead + Unpin> Unpin for InterleavePending<R> {}
17+
18+
impl<R: AsyncRead> InterleavePending<R> {
19+
unsafe_pinned!(reader: R);
20+
unsafe_unpinned!(pended: bool);
21+
22+
pub(crate) fn new(reader: R) -> InterleavePending<R> {
23+
InterleavePending {
24+
reader,
25+
pended: false,
26+
}
27+
}
28+
29+
fn project<'a>(self: Pin<&'a mut Self>) -> (Pin<&'a mut R>, &'a mut bool) {
30+
unsafe {
31+
let this = self.get_unchecked_mut();
32+
(Pin::new_unchecked(&mut this.reader), &mut this.pended)
33+
}
34+
}
35+
}
36+
37+
impl<R: AsyncRead> AsyncRead for InterleavePending<R> {
38+
fn poll_read(
39+
self: Pin<&mut Self>,
40+
cx: &mut Context<'_>,
41+
buf: &mut [u8],
42+
) -> Poll<io::Result<usize>> {
43+
let (reader, pended) = self.project();
44+
if *pended {
45+
let next = reader.poll_read(cx, buf);
46+
if next.is_ready() {
47+
*pended = false;
48+
}
49+
next
50+
} else {
51+
cx.waker().wake_by_ref();
52+
*pended = true;
53+
Poll::Pending
54+
}
55+
}
56+
}
57+
58+
impl<R: AsyncBufRead> AsyncBufRead for InterleavePending<R> {
59+
fn poll_fill_buf<'a>(
60+
self: Pin<&'a mut Self>,
61+
cx: &mut Context<'_>,
62+
) -> Poll<io::Result<&'a [u8]>> {
63+
let (reader, pended) = self.project();
64+
if *pended {
65+
let next = reader.poll_fill_buf(cx);
66+
if next.is_ready() {
67+
*pended = false;
68+
}
69+
next
70+
} else {
71+
cx.waker().wake_by_ref();
72+
*pended = true;
73+
Poll::Pending
74+
}
75+
}
76+
77+
fn consume(self: Pin<&mut Self>, amount: usize) {
78+
self.reader().consume(amount)
79+
}
80+
}

futures-test/src/io/mod.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//! Additional combinators for testing async IO.
2+
3+
use futures_io::AsyncRead;
4+
5+
mod interleave_pending;
6+
pub use self::interleave_pending::InterleavePending;
7+
8+
/// Additional combinators for testing async readers.
9+
pub trait AsyncReadTestExt: AsyncRead {
10+
/// Introduces an extra [`Poll::Pending`](futures_core::task::Poll::Pending)
11+
/// in between each read of the reader.
12+
///
13+
/// # Examples
14+
///
15+
/// ```
16+
/// #![feature(async_await)]
17+
/// use futures::task::Poll;
18+
/// use futures::io::AsyncRead;
19+
/// use futures_test::task::noop_context;
20+
/// use futures_test::io::AsyncReadTestExt;
21+
/// use pin_utils::pin_mut;
22+
///
23+
/// let reader = std::io::Cursor::new(&[1, 2, 3]).interleave_pending();
24+
/// pin_mut!(reader);
25+
///
26+
/// let mut cx = noop_context();
27+
///
28+
/// let mut buf = [0, 0];
29+
///
30+
/// assert_eq!(reader.as_mut().poll_read(&mut cx, &mut buf[..])?, Poll::Pending);
31+
/// assert_eq!(reader.as_mut().poll_read(&mut cx, &mut buf[..])?, Poll::Ready(2));
32+
/// assert_eq!(buf, [1, 2]);
33+
/// assert_eq!(reader.as_mut().poll_read(&mut cx, &mut buf[..])?, Poll::Pending);
34+
/// assert_eq!(reader.as_mut().poll_read(&mut cx, &mut buf[..])?, Poll::Ready(1));
35+
/// assert_eq!(buf, [3, 2]);
36+
/// assert_eq!(reader.as_mut().poll_read(&mut cx, &mut buf[..])?, Poll::Pending);
37+
/// assert_eq!(reader.as_mut().poll_read(&mut cx, &mut buf[..])?, Poll::Ready(0));
38+
///
39+
/// # Ok::<(), std::io::Error>(())
40+
/// ```
41+
///
42+
/// ## `AsyncBufRead`
43+
///
44+
/// The returned reader will also implement `AsyncBufRead` if the underlying reader does.
45+
///
46+
/// ```
47+
/// #![feature(async_await)]
48+
/// use futures::task::Poll;
49+
/// use futures::io::AsyncBufRead;
50+
/// use futures_test::task::noop_context;
51+
/// use futures_test::io::AsyncReadTestExt;
52+
/// use pin_utils::pin_mut;
53+
///
54+
/// let reader = std::io::Cursor::new(&[1, 2, 3]).interleave_pending();
55+
/// pin_mut!(reader);
56+
///
57+
/// let mut cx = noop_context();
58+
///
59+
/// assert_eq!(reader.as_mut().poll_fill_buf(&mut cx)?, Poll::Pending);
60+
/// assert_eq!(reader.as_mut().poll_fill_buf(&mut cx)?, Poll::Ready(&[1, 2, 3][..]));
61+
/// reader.as_mut().consume(2);
62+
/// assert_eq!(reader.as_mut().poll_fill_buf(&mut cx)?, Poll::Pending);
63+
/// assert_eq!(reader.as_mut().poll_fill_buf(&mut cx)?, Poll::Ready(&[3][..]));
64+
/// reader.as_mut().consume(1);
65+
/// assert_eq!(reader.as_mut().poll_fill_buf(&mut cx)?, Poll::Pending);
66+
/// assert_eq!(reader.as_mut().poll_fill_buf(&mut cx)?, Poll::Ready(&[][..]));
67+
///
68+
/// # Ok::<(), std::io::Error>(())
69+
/// ```
70+
fn interleave_pending(self) -> InterleavePending<Self>
71+
where
72+
Self: Sized,
73+
{
74+
InterleavePending::new(self)
75+
}
76+
}
77+
78+
impl<R> AsyncReadTestExt for R where R: AsyncRead {}

futures-test/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,9 @@ pub mod task;
2626

2727
#[cfg(feature = "std")]
2828
pub mod future;
29+
30+
#[cfg(feature = "std")]
31+
pub mod stream;
32+
33+
#[cfg(feature = "std")]
34+
pub mod io;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use futures_core::stream::Stream;
2+
use pin_utils::{unsafe_pinned, unsafe_unpinned};
3+
use std::{
4+
marker::Unpin,
5+
pin::Pin,
6+
task::{Context, Poll},
7+
};
8+
9+
/// Stream for the [`interleave_pending`](super::StreamTestExt::interleave_pending) method.
10+
#[derive(Debug)]
11+
pub struct InterleavePending<St: Stream> {
12+
stream: St,
13+
pended: bool,
14+
}
15+
16+
impl<St: Stream + Unpin> Unpin for InterleavePending<St> {}
17+
18+
impl<St: Stream> InterleavePending<St> {
19+
unsafe_pinned!(stream: St);
20+
unsafe_unpinned!(pended: bool);
21+
22+
pub(crate) fn new(stream: St) -> InterleavePending<St> {
23+
InterleavePending {
24+
stream,
25+
pended: false,
26+
}
27+
}
28+
}
29+
30+
impl<St: Stream> Stream for InterleavePending<St> {
31+
type Item = St::Item;
32+
33+
fn poll_next(
34+
mut self: Pin<&mut Self>,
35+
cx: &mut Context<'_>,
36+
) -> Poll<Option<Self::Item>> {
37+
if *self.as_mut().pended() {
38+
let next = self.as_mut().stream().poll_next(cx);
39+
if next.is_ready() {
40+
*self.pended() = false;
41+
}
42+
next
43+
} else {
44+
cx.waker().wake_by_ref();
45+
*self.pended() = true;
46+
Poll::Pending
47+
}
48+
}
49+
}

futures-test/src/stream/mod.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//! Additional combinators for testing streams.
2+
3+
use futures_core::stream::Stream;
4+
5+
mod interleave_pending;
6+
pub use self::interleave_pending::InterleavePending;
7+
8+
/// Additional combinators for testing streams.
9+
pub trait StreamTestExt: Stream {
10+
/// Introduces an extra [`Poll::Pending`](futures_core::task::Poll::Pending)
11+
/// in between each item of the stream.
12+
///
13+
/// # Examples
14+
///
15+
/// ```
16+
/// #![feature(async_await)]
17+
/// use futures::task::Poll;
18+
/// use futures::stream::{self, Stream};
19+
/// use futures_test::task::noop_context;
20+
/// use futures_test::stream::StreamTestExt;
21+
/// use pin_utils::pin_mut;
22+
///
23+
/// let stream = stream::iter(vec![1, 2]).interleave_pending();
24+
/// pin_mut!(stream);
25+
///
26+
/// let mut cx = noop_context();
27+
///
28+
/// assert_eq!(stream.as_mut().poll_next(&mut cx), Poll::Pending);
29+
/// assert_eq!(stream.as_mut().poll_next(&mut cx), Poll::Ready(Some(1)));
30+
/// assert_eq!(stream.as_mut().poll_next(&mut cx), Poll::Pending);
31+
/// assert_eq!(stream.as_mut().poll_next(&mut cx), Poll::Ready(Some(2)));
32+
/// assert_eq!(stream.as_mut().poll_next(&mut cx), Poll::Pending);
33+
/// assert_eq!(stream.as_mut().poll_next(&mut cx), Poll::Ready(None));
34+
/// ```
35+
fn interleave_pending(self) -> InterleavePending<Self>
36+
where
37+
Self: Sized,
38+
{
39+
InterleavePending::new(self)
40+
}
41+
}
42+
43+
impl<St> StreamTestExt for St where St: Stream {}

0 commit comments

Comments
 (0)