summaryrefslogtreecommitdiffstats
path: root/tokio-test
diff options
context:
space:
mode:
authorBenjamin Halsted <benjamin_halsted@yahoo.com>2020-04-02 14:10:12 -0700
committerGitHub <noreply@github.com>2020-04-02 17:10:12 -0400
commitcf4cbc142bd8198d2112cf671c120740fdc4e132 (patch)
treeff9b3f1d3f1427a3a724c988793a2400c7a523d6 /tokio-test
parent215d7d4c5f3aa5b436183b8d8abfb9701f34a17d (diff)
test: Added read_error() and write_error() (#2337)
Enable testing of edge cases caused by io errors.
Diffstat (limited to 'tokio-test')
-rw-r--r--tokio-test/src/io.rs66
-rw-r--r--tokio-test/tests/io.rs47
2 files changed, 111 insertions, 2 deletions
diff --git a/tokio-test/src/io.rs b/tokio-test/src/io.rs
index 9a3dbe04..8af6a9f6 100644
--- a/tokio-test/src/io.rs
+++ b/tokio-test/src/io.rs
@@ -27,6 +27,7 @@ use futures_core::ready;
use std::collections::VecDeque;
use std::future::Future;
use std::pin::Pin;
+use std::sync::Arc;
use std::task::{self, Poll, Waker};
use std::{cmp, io};
@@ -57,6 +58,10 @@ enum Action {
Read(Vec<u8>),
Write(Vec<u8>),
Wait(Duration),
+ // Wrapped in Arc so that Builder can be cloned and Send.
+ // Mock is not cloned as does not need to check Rc for ref counts.
+ ReadError(Option<Arc<io::Error>>),
+ WriteError(Option<Arc<io::Error>>),
}
#[derive(Debug)]
@@ -83,6 +88,16 @@ impl Builder {
self
}
+ /// Sequence a `read` operation that produces an error.
+ ///
+ /// The next operation in the mock's script will be to expect a `read` call
+ /// and return `error`.
+ pub fn read_error(&mut self, error: io::Error) -> &mut Self {
+ let error = Some(error.into());
+ self.actions.push_back(Action::ReadError(error));
+ self
+ }
+
/// Sequence a `write` operation.
///
/// The next operation in the mock's script will be to expect a `write`
@@ -92,6 +107,16 @@ impl Builder {
self
}
+ /// Sequence a `write` operation that produces an error.
+ ///
+ /// The next operation in the mock's script will be to expect a `write`
+ /// call that provides `error`.
+ pub fn write_error(&mut self, error: io::Error) -> &mut Self {
+ let error = Some(error.into());
+ self.actions.push_back(Action::WriteError(error));
+ self
+ }
+
/// Sequence a wait.
///
/// The next operation in the mock's script will be to wait without doing so
@@ -128,6 +153,16 @@ impl Handle {
self
}
+ /// Sequence a `read` operation error.
+ ///
+ /// The next operation in the mock's script will be to expect a `read` call
+ /// and return `error`.
+ pub fn read_error(&mut self, error: io::Error) -> &mut Self {
+ let error = Some(error.into());
+ self.tx.send(Action::ReadError(error)).unwrap();
+ self
+ }
+
/// Sequence a `write` operation.
///
/// The next operation in the mock's script will be to expect a `write`
@@ -136,6 +171,16 @@ impl Handle {
self.tx.send(Action::Write(buf.into())).unwrap();
self
}
+
+ /// Sequence a `write` operation error.
+ ///
+ /// The next operation in the mock's script will be to expect a `write`
+ /// call error.
+ pub fn write_error(&mut self, error: io::Error) -> &mut Self {
+ let error = Some(error.into());
+ self.tx.send(Action::WriteError(error)).unwrap();
+ self
+ }
}
impl Inner {
@@ -174,6 +219,12 @@ impl Inner {
// Return the number of bytes read
Ok(n)
}
+ Some(&mut Action::ReadError(ref mut err)) => {
+ // As the
+ let err = err.take().expect("Should have been removed from actions.");
+ let err = Arc::try_unwrap(err).expect("There are no other references.");
+ Err(err)
+ }
Some(_) => {
// Either waiting or expecting a write
Err(io::ErrorKind::WouldBlock.into())
@@ -193,6 +244,12 @@ impl Inner {
return Err(io::ErrorKind::WouldBlock.into());
}
+ if let Some(&mut Action::WriteError(ref mut err)) = self.action() {
+ let err = err.take().expect("Should have been removed from actions.");
+ let err = Arc::try_unwrap(err).expect("There are no other references.");
+ return Err(err);
+ }
+
for i in 0..self.actions.len() {
match self.actions[i] {
Action::Write(ref mut expect) => {
@@ -210,7 +267,7 @@ impl Inner {
return Ok(ret);
}
}
- Action::Wait(..) => {
+ Action::Wait(..) | Action::WriteError(..) => {
break;
}
_ => {}
@@ -258,6 +315,11 @@ impl Inner {
break;
}
}
+ Action::ReadError(ref mut error) | Action::WriteError(ref mut error) => {
+ if error.is_some() {
+ break;
+ }
+ }
}
let _action = self.actions.pop_front();
@@ -272,7 +334,7 @@ impl Inner {
impl Mock {
fn maybe_wakeup_reader(&mut self) {
match self.inner.action() {
- Some(&mut Action::Read(_)) | None => {
+ Some(&mut Action::Read(_)) | Some(&mut Action::ReadError(_)) | None => {
if let Some(waker) = self.inner.read_wait.take() {
waker.wake();
}
diff --git a/tokio-test/tests/io.rs b/tokio-test/tests/io.rs
index 954bb469..948bc323 100644
--- a/tokio-test/tests/io.rs
+++ b/tokio-test/tests/io.rs
@@ -1,5 +1,6 @@
#![warn(rust_2018_idioms)]
+use std::io;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio_test::io::Builder;
@@ -17,9 +18,55 @@ async fn read() {
}
#[tokio::test]
+async fn read_error() {
+ let error = io::Error::new(io::ErrorKind::Other, "cruel");
+ let mut mock = Builder::new()
+ .read(b"hello ")
+ .read_error(error)
+ .read(b"world!")
+ .build();
+ let mut buf = [0; 256];
+
+ let n = mock.read(&mut buf).await.expect("read 1");
+ assert_eq!(&buf[..n], b"hello ");
+
+ match mock.read(&mut buf).await {
+ Err(error) => {
+ assert_eq!(error.kind(), io::ErrorKind::Other);
+ assert_eq!("cruel", format!("{}", error));
+ }
+ Ok(_) => panic!("error not received"),
+ }
+
+ let n = mock.read(&mut buf).await.expect("read 1");
+ assert_eq!(&buf[..n], b"world!");
+}
+
+#[tokio::test]
async fn write() {
let mut mock = Builder::new().write(b"hello ").write(b"world!").build();
mock.write_all(b"hello ").await.expect("write 1");
mock.write_all(b"world!").await.expect("write 2");
}
+
+#[tokio::test]
+async fn write_error() {
+ let error = io::Error::new(io::ErrorKind::Other, "cruel");
+ let mut mock = Builder::new()
+ .write(b"hello ")
+ .write_error(error)
+ .write(b"world!")
+ .build();
+ mock.write_all(b"hello ").await.expect("write 1");
+
+ match mock.write_all(b"whoa").await {
+ Err(error) => {
+ assert_eq!(error.kind(), io::ErrorKind::Other);
+ assert_eq!("cruel", format!("{}", error));
+ }
+ Ok(_) => panic!("error not received"),
+ }
+
+ mock.write_all(b"world!").await.expect("write 2");
+}