Info
This post is auto-generated from RSS feed The Rust Programming Language Forum - Latest topics. Source: Is it possible to make mocked AsyncWrite and AsyncRead behavior use toio::time::sleep() and other async functions?
Thanks to lots of help from the community over in this question, I've managed to get a mocked test rig up and running that allows for arbitrary behavior when AsyncWrite and AsyncRead poll_* functions are called by the AsyncReadExt and AsyncWriteExt convenience wrappers. However, I never actually noticed until now that the functions of the aforementioned traits are not themselves marked async; a little digging suggests Rust doesn't fully support async trait functions yet? Regardless, this presents a problem as I was hoping to simulate I/O timeout with a call to tokio::time::sleep() inside my mocked poll_write() implementation, as follows:
trait AsyncReadWrite: AsyncRead + AsyncWrite + Unpin {}
mock! {
AsyncReadWrite {}
impl AsyncRead for AsyncReadWrite {
fn poll_read<'ctx, 'buf>(
self: Pin<&mut Self>,
cx: &mut std::task::Context<'ctx>,
buf: &mut ReadBuf<'buf>,
) -> Poll<Result<(), std::io::Error>>;
}
impl AsyncWrite for AsyncReadWrite {
fn poll_write<'a>(
self: Pin<&mut Self>,
cx: &mut std::task::Context<'a>,
buf: &[u8],
) -> Poll<Result<usize, std::io::Error>>;
fn poll_flush<'a>(
self: Pin<&mut Self>,
cx: &mut std::task::Context<'a>,
) -> Poll<Result<(), std::io::Error>>;
fn poll_shutdown<'a>(
self: Pin<&mut Self>,
cx: &mut std::task::Context<'a>,
) -> Poll<Result<(), std::io::Error>>;
}
}
#[tokio::test]
async fn test_timeout() {
struct MockNetworkerTimeout {}
impl Networker for MockNetworkerTimeout {
async fn connect<A: ToSocketAddrs>(
&self,
_addr: A,
) -> std::io::Result<impl AsyncRead + AsyncWrite + Unpin> {
let mut mock_stream = MockAsyncReadWrite::new();
mock_stream
.expect_poll_read()
.returning(|_, _| Poll::Ready(std::io::Result::Ok(())));
mock_stream.expect_poll_write().returning(|ctx, buf: &[u8]| {
dbg!("mock async writing timeout");
sleep(Duration::from_secs(10)).await;
Poll::Ready(Ok(1))
});
std::io::Result::Ok(mock_stream)
}
}
let mock_networker = MockNetworkerTimeout {};
match timeout(
Duration::from_secs(5),
// send_comms calls mock_networker.connect() and then calls AsyncWriteExt's write() on the resultant opaque AsyncRead + AsyncWrite + Unpin impl
send_comms(&vec![0u8; 1024], &mock_networker)
).await {
Ok(_) => panic!("send_comms succeeded despite delay of 10 seconds that should have caused timeout"),
Err(e) => {
dbg!(format!("Timed out as expected with {}", e));
},
}
}
But that fails to compile with the error:
error[E0728]: `await` is only allowed inside `async` functions and blocks
--> ...
|
1790 | mock_stream.expect_poll_write().returning(|ctx, buf: &[u8]| {
| ----------------- this is not `async`
...
1796 | sleep(Duration::from_secs(10)).await;
| ^^^^^ only allowed inside `async` functions and blocks
I can still simulate timeout by simply having the expect_poll_write() return Poll::Pending exclusively, but it would be nice to have a timing mechanism on the inside of the mocked behavior. Is there a way to get a handle to the expected async calling context inside these non-async trait functions?
2 posts - 2 participants
🏷️ rust_feed