Warning
This post was published 61 days ago. The information described in this article may have changed.
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