diff options
author | Benjamin Halsted <benjamin_halsted@yahoo.com> | 2020-04-02 14:10:12 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-02 17:10:12 -0400 |
commit | cf4cbc142bd8198d2112cf671c120740fdc4e132 (patch) | |
tree | ff9b3f1d3f1427a3a724c988793a2400c7a523d6 | |
parent | 215d7d4c5f3aa5b436183b8d8abfb9701f34a17d (diff) |
test: Added read_error() and write_error() (#2337)
Enable testing of edge cases caused by io errors.
-rw-r--r-- | tokio-test/src/io.rs | 66 | ||||
-rw-r--r-- | tokio-test/tests/io.rs | 47 |
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"); +} |