Remove mock-io git dependency
This commit is contained in:
		
							
								
								
									
										586
									
								
								tests/support/mock_io.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										586
									
								
								tests/support/mock_io.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,586 @@ | ||||
| //! A mock type implementing [`Read`] and [`Write`]. | ||||
| //! | ||||
| //! Copied from https://github.com/carllerche/mock-io. | ||||
| //! | ||||
| //! TODO: | ||||
| //! - Either the mock-io crate should be released or this module should be | ||||
| //!   removed from h2. | ||||
| //! | ||||
| //! # Overview | ||||
| //! | ||||
| //! Provides a type that implements [`Read`] + [`Write`] that can be configured | ||||
| //! to handle an arbitrary sequence of read and write operations. This is useful | ||||
| //! for writing unit tests for networking services as using an actual network | ||||
| //! type is fairly non deterministic. | ||||
| //! | ||||
| //! # Usage | ||||
| //! | ||||
| //! Add the following to your `Cargo.toml` | ||||
| //! | ||||
| //! ```toml | ||||
| //! [dependencies] | ||||
| //! mock-io = { git = "https://github.com/carllerche/mock-io" } | ||||
| //! ``` | ||||
| //! | ||||
| //! Then use it in your project. For example, a test could be written: | ||||
| //! | ||||
| //! ``` | ||||
| //! extern crate mock_io; | ||||
| //! | ||||
| //! use mock_io::{Builder, Mock}; | ||||
| //! use std::io::{Read, Write}; | ||||
| //! | ||||
| //! # /* | ||||
| //! #[test] | ||||
| //! # */ | ||||
| //! fn test_io() { | ||||
| //!     let mut mock = Builder::new() | ||||
| //!         .write(b"ping") | ||||
| //!         .read(b"pong") | ||||
| //!         .build(); | ||||
| //! | ||||
| //!    let n = mock.write(b"ping").unwrap(); | ||||
| //!    assert_eq!(n, 4); | ||||
| //! | ||||
| //!    let mut buf = vec![]; | ||||
| //!    mock.read_to_end(&mut buf).unwrap(); | ||||
| //! | ||||
| //!    assert_eq!(buf, b"pong"); | ||||
| //! } | ||||
| //! # pub fn main() { | ||||
| //! # test_io(); | ||||
| //! # } | ||||
| //! ``` | ||||
| //! | ||||
| //! Attempting to write data that the mock isn't expected will result in a | ||||
| //! panic. | ||||
| //! | ||||
| //! # Tokio | ||||
| //! | ||||
| //! `Mock` also supports tokio by implementing `AsyncRead` and `AsyncWrite`. | ||||
| //! When using `Mock` in context of a Tokio task, it will automatically switch | ||||
| //! to "async" behavior (this can also be set explicitly by calling `set_async` | ||||
| //! on `Builder`). | ||||
| //! | ||||
| //! In async mode, calls to read and write are non-blocking and the task using | ||||
| //! the mock is notified when the readiness state changes. | ||||
| //! | ||||
| //! # `io-dump` dump files | ||||
| //! | ||||
| //! `Mock` can also be configured from an `io-dump` file. By doing this, the | ||||
| //! mock value will replay a previously recorded behavior. This is useful for | ||||
| //! collecting a scenario from the real world and replying it as part of a test. | ||||
| //! | ||||
| //! [`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html | ||||
| //! [`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html | ||||
|  | ||||
| #![allow(deprecated)] | ||||
|  | ||||
| use std::{cmp, io}; | ||||
| use std::collections::VecDeque; | ||||
| use std::time::{Duration, Instant}; | ||||
|  | ||||
| /// An I/O handle that follows a predefined script. | ||||
| /// | ||||
| /// This value is created by `Builder` and implements `Read + `Write`. It | ||||
| /// follows the scenario described by the builder and panics otherwise. | ||||
| #[derive(Debug)] | ||||
| pub struct Mock { | ||||
|     inner: Inner, | ||||
|     tokio: tokio::Inner, | ||||
|     async: Option<bool>, | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub struct Handle { | ||||
|     inner: tokio::Handle, | ||||
| } | ||||
|  | ||||
| /// Builds `Mock` instances. | ||||
| #[derive(Debug, Clone, Default)] | ||||
| pub struct Builder { | ||||
|     // Sequence of actions for the Mock to take | ||||
|     actions: VecDeque<Action>, | ||||
|  | ||||
|     // true for Tokio, false for blocking, None to auto detect | ||||
|     async: Option<bool>, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone)] | ||||
| enum Action { | ||||
|     Read(Vec<u8>), | ||||
|     Write(Vec<u8>), | ||||
|     Wait(Duration), | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| struct Inner { | ||||
|     actions: VecDeque<Action>, | ||||
|     waiting: Option<Instant>, | ||||
| } | ||||
|  | ||||
| impl Builder { | ||||
|     /// Return a new, empty `Builder. | ||||
|     pub fn new() -> Self { | ||||
|         Self::default() | ||||
|     } | ||||
|  | ||||
|     /// Sequence a `read` operation. | ||||
|     /// | ||||
|     /// The next operation in the mock's script will be to expect a `read` call | ||||
|     /// and return `buf`. | ||||
|     pub fn read(&mut self, buf: &[u8]) -> &mut Self { | ||||
|         self.actions.push_back(Action::Read(buf.into())); | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /// Sequence a `write` operation. | ||||
|     /// | ||||
|     /// The next operation in the mock's script will be to expect a `write` | ||||
|     /// call. | ||||
|     pub fn write(&mut self, buf: &[u8]) -> &mut Self { | ||||
|         self.actions.push_back(Action::Write(buf.into())); | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /// Sequence a wait. | ||||
|     /// | ||||
|     /// The next operation in the mock's script will be to wait without doing so | ||||
|     /// for `duration` amount of time. | ||||
|     pub fn wait(&mut self, duration: Duration) -> &mut Self { | ||||
|         let duration = cmp::max(duration, Duration::from_millis(1)); | ||||
|         self.actions.push_back(Action::Wait(duration)); | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /// Build a `Mock` value according to the defined script. | ||||
|     pub fn build(&mut self) -> Mock { | ||||
|         let (mock, _) = self.build_with_handle(); | ||||
|         mock | ||||
|     } | ||||
|  | ||||
|     /// Build a `Mock` value paired with a handle | ||||
|     pub fn build_with_handle(&mut self) -> (Mock, Handle) { | ||||
|         let (tokio, handle) = tokio::Inner::new(); | ||||
|  | ||||
|         let src = self.clone(); | ||||
|  | ||||
|         let mock = Mock { | ||||
|             inner: Inner { | ||||
|                 actions: src.actions, | ||||
|                 waiting: None, | ||||
|             }, | ||||
|             tokio: tokio, | ||||
|             async: src.async, | ||||
|         }; | ||||
|  | ||||
|         let handle = Handle { inner: handle }; | ||||
|  | ||||
|         (mock, handle) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Handle { | ||||
|     /// Sequence a `read` operation. | ||||
|     /// | ||||
|     /// The next operation in the mock's script will be to expect a `read` call | ||||
|     /// and return `buf`. | ||||
|     pub fn read(&mut self, buf: &[u8]) -> &mut Self { | ||||
|         self.inner.read(buf); | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /// Sequence a `write` operation. | ||||
|     /// | ||||
|     /// The next operation in the mock's script will be to expect a `write` | ||||
|     /// call. | ||||
|     pub fn write(&mut self, buf: &[u8]) -> &mut Self { | ||||
|         self.inner.write(buf); | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Mock { | ||||
|     fn sync_read(&mut self, dst: &mut [u8]) -> io::Result<usize> { | ||||
|         use std::thread; | ||||
|  | ||||
|         loop { | ||||
|             match self.inner.read(dst) { | ||||
|                 Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { | ||||
|                     if let Some(rem) = self.inner.remaining_wait() { | ||||
|                         thread::sleep(rem); | ||||
|                     } else { | ||||
|                         // We've entered a dead lock scenario. The peer expects | ||||
|                         // a write but we are reading. | ||||
|                         panic!("mock_io::Mock expects write but currently blocked in read"); | ||||
|                     } | ||||
|                 } | ||||
|                 ret => return ret, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn sync_write(&mut self, src: &[u8]) -> io::Result<usize> { | ||||
|         match self.inner.write(src) { | ||||
|             Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { | ||||
|                 panic!("mock_io::Mock not currently expecting a write"); | ||||
|             } | ||||
|             ret => ret, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Returns `true` if running in a futures-rs task context | ||||
|     fn is_async(&self) -> bool { | ||||
|         self.async.unwrap_or(tokio::is_task_ctx()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Inner { | ||||
|     fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> { | ||||
|         match self.action() { | ||||
|             Some(&mut Action::Read(ref mut data)) =>{ | ||||
|                 // Figure out how much to copy | ||||
|                 let n = cmp::min(dst.len(), data.len()); | ||||
|  | ||||
|                 // Copy the data into the `dst` slice | ||||
|                 (&mut dst[..n]).copy_from_slice(&data[..n]); | ||||
|  | ||||
|                 // Drain the data from the source | ||||
|                 data.drain(..n); | ||||
|  | ||||
|                 // Return the number of bytes read | ||||
|                 Ok(n) | ||||
|             } | ||||
|             Some(_) => { | ||||
|                 // Either waiting or expecting a write | ||||
|                 Err(io::ErrorKind::WouldBlock.into()) | ||||
|             } | ||||
|             None => { | ||||
|                  Ok(0) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn write(&mut self, mut src: &[u8]) -> io::Result<usize> { | ||||
|         let mut ret = 0; | ||||
|  | ||||
|         if self.actions.is_empty() { | ||||
|             return Err(io::ErrorKind::BrokenPipe.into()); | ||||
|         } | ||||
|  | ||||
|         match self.action() { | ||||
|             Some(&mut Action::Wait(..)) => { | ||||
|                 return Err(io::ErrorKind::WouldBlock.into()); | ||||
|             } | ||||
|             _ => {} | ||||
|         } | ||||
|  | ||||
|         for i in 0..self.actions.len() { | ||||
|             match self.actions[i] { | ||||
|                 Action::Write(ref mut expect) => { | ||||
|                     let n = cmp::min(src.len(), expect.len()); | ||||
|  | ||||
|                     assert_eq!(&src[..n], &expect[..n]); | ||||
|  | ||||
|                     // Drop data that was matched | ||||
|                     expect.drain(..n); | ||||
|                     src = &src[n..]; | ||||
|  | ||||
|                     ret += n; | ||||
|  | ||||
|                     if src.is_empty() { | ||||
|                         return Ok(ret); | ||||
|                     } | ||||
|                 } | ||||
|                 Action::Wait(..) => { | ||||
|                     break; | ||||
|                 } | ||||
|                 _ => {} | ||||
|             } | ||||
|  | ||||
|             // TODO: remove write | ||||
|         } | ||||
|  | ||||
|         Ok(ret) | ||||
|     } | ||||
|  | ||||
|     fn remaining_wait(&mut self) -> Option<Duration> { | ||||
|         match self.action() { | ||||
|             Some(&mut Action::Wait(dur)) => Some(dur), | ||||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn action(&mut self) -> Option<&mut Action> { | ||||
|         loop { | ||||
|             if self.actions.is_empty() { | ||||
|                 return None; | ||||
|             } | ||||
|  | ||||
|             match self.actions[0] { | ||||
|                 Action::Read(ref mut data) => { | ||||
|                     if !data.is_empty() { | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 Action::Write(ref mut data) => { | ||||
|                     if !data.is_empty() { | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 Action::Wait(ref mut dur) => { | ||||
|                     if let Some(until) = self.waiting { | ||||
|                         let now = Instant::now(); | ||||
|  | ||||
|                         if now < until { | ||||
|                             break; | ||||
|                         } | ||||
|                     } else { | ||||
|                         self.waiting = Some(Instant::now() + *dur); | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             let _action = self.actions.pop_front(); | ||||
|         } | ||||
|  | ||||
|         self.actions.front_mut() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl io::Read for Mock { | ||||
|     fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> { | ||||
|         if self.is_async() { | ||||
|             tokio::async_read(self, dst) | ||||
|         } else { | ||||
|             self.sync_read(dst) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl io::Write for Mock { | ||||
|     fn write(&mut self, src: &[u8]) -> io::Result<usize> { | ||||
|         if self.is_async() { | ||||
|             tokio::async_write(self, src) | ||||
|         } else { | ||||
|             self.sync_write(src) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn flush(&mut self) -> io::Result<()> { | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| // use tokio::*; | ||||
|  | ||||
| mod tokio { | ||||
|     extern crate futures; | ||||
|     extern crate tokio_io; | ||||
|     extern crate tokio_timer; | ||||
|  | ||||
|     use super::*; | ||||
|  | ||||
|     use self::futures::{Future, Stream, Poll, Async}; | ||||
|     use self::futures::sync::mpsc; | ||||
|     use self::futures::task::{self, Task}; | ||||
|     use self::tokio_io::{AsyncRead, AsyncWrite}; | ||||
|     use self::tokio_timer::{Timer, Sleep}; | ||||
|  | ||||
|     use std::io; | ||||
|  | ||||
|     impl Builder { | ||||
|         pub fn set_async(&mut self, is_async: bool) -> &mut Self { | ||||
|             self.async = Some(is_async); | ||||
|             self | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[derive(Debug)] | ||||
|     pub struct Inner { | ||||
|         timer: Timer, | ||||
|         sleep: Option<Sleep>, | ||||
|         read_wait: Option<Task>, | ||||
|         rx: mpsc::UnboundedReceiver<Action>, | ||||
|     } | ||||
|  | ||||
|     #[derive(Debug)] | ||||
|     pub struct Handle { | ||||
|         tx: mpsc::UnboundedSender<Action>, | ||||
|     } | ||||
|  | ||||
|     // ===== impl Handle ===== | ||||
|  | ||||
|     impl Handle { | ||||
|         pub fn read(&mut self, buf: &[u8]) { | ||||
|             mpsc::UnboundedSender::send(&mut self.tx, Action::Read(buf.into())).unwrap(); | ||||
|         } | ||||
|  | ||||
|         pub fn write(&mut self, buf: &[u8]) { | ||||
|             mpsc::UnboundedSender::send(&mut self.tx, Action::Write(buf.into())).unwrap(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // ===== impl Inner ===== | ||||
|  | ||||
|     impl Inner { | ||||
|         pub fn new() -> (Inner, Handle) { | ||||
|             // TODO: We probably want a higher resolution timer. | ||||
|             let timer = tokio_timer::wheel() | ||||
|                 .tick_duration(Duration::from_millis(1)) | ||||
|                 .max_timeout(Duration::from_secs(3600)) | ||||
|                 .build(); | ||||
|  | ||||
|             let (tx, rx) = mpsc::unbounded(); | ||||
|  | ||||
|             let inner = Inner { | ||||
|                 timer: timer, | ||||
|                 sleep: None, | ||||
|                 read_wait: None, | ||||
|                 rx: rx, | ||||
|             }; | ||||
|  | ||||
|             let handle = Handle { tx }; | ||||
|  | ||||
|             (inner, handle) | ||||
|         } | ||||
|  | ||||
|         pub(super) fn poll_action(&mut self) -> Poll<Option<Action>, ()> { | ||||
|             self.rx.poll() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl Mock { | ||||
|         fn maybe_wakeup_reader(&mut self) { | ||||
|             match self.inner.action() { | ||||
|                 Some(&mut Action::Read(_)) | None => { | ||||
|                     if let Some(task) = self.tokio.read_wait.take() { | ||||
|                         task.notify(); | ||||
|                     } | ||||
|                 } | ||||
|                 _ => {} | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn async_read(me: &mut Mock, dst: &mut [u8]) -> io::Result<usize> { | ||||
|         loop { | ||||
|             if let Some(ref mut sleep) = me.tokio.sleep { | ||||
|                 let res = try!(sleep.poll()); | ||||
|  | ||||
|                 if !res.is_ready() { | ||||
|                     return Err(io::ErrorKind::WouldBlock.into()); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // If a sleep is set, it has already fired | ||||
|             me.tokio.sleep = None; | ||||
|  | ||||
|             match me.inner.read(dst) { | ||||
|                 Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { | ||||
|                     if let Some(rem) = me.inner.remaining_wait() { | ||||
|                         me.tokio.sleep = Some(me.tokio.timer.sleep(rem)); | ||||
|                     } else { | ||||
|                         me.tokio.read_wait = Some(task::current()); | ||||
|                         return Err(io::ErrorKind::WouldBlock.into()); | ||||
|                     } | ||||
|                 } | ||||
|                 Ok(0) => { | ||||
|                     // TODO: Extract | ||||
|                     match me.tokio.poll_action().unwrap() { | ||||
|                         Async::Ready(Some(action)) => { | ||||
|                             me.inner.actions.push_back(action); | ||||
|                             continue; | ||||
|                         } | ||||
|                         Async::Ready(None) => { | ||||
|                             return Ok(0); | ||||
|                         } | ||||
|                         Async::NotReady => { | ||||
|                             return Err(io::ErrorKind::WouldBlock.into()); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 ret => return ret, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn async_write(me: &mut Mock, src: &[u8]) -> io::Result<usize> { | ||||
|         loop { | ||||
|             if let Some(ref mut sleep) = me.tokio.sleep { | ||||
|                 let res = try!(sleep.poll()); | ||||
|  | ||||
|                 if !res.is_ready() { | ||||
|                     return Err(io::ErrorKind::WouldBlock.into()); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // If a sleep is set, it has already fired | ||||
|             me.tokio.sleep = None; | ||||
|  | ||||
|             match me.inner.write(src) { | ||||
|                 Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { | ||||
|                     if let Some(rem) = me.inner.remaining_wait() { | ||||
|                         me.tokio.sleep = Some(me.tokio.timer.sleep(rem)); | ||||
|                     } else { | ||||
|                         panic!("unexpected WouldBlock"); | ||||
|                     } | ||||
|                 } | ||||
|                 Ok(0) => { | ||||
|                     // TODO: Is this correct? | ||||
|                     if !me.inner.actions.is_empty() { | ||||
|                         return Err(io::ErrorKind::WouldBlock.into()); | ||||
|                     } | ||||
|  | ||||
|                     // TODO: Extract | ||||
|                     match me.tokio.poll_action().unwrap() { | ||||
|                         Async::Ready(Some(action)) => { | ||||
|                             me.inner.actions.push_back(action); | ||||
|                             continue; | ||||
|                         } | ||||
|                         Async::Ready(None) => { | ||||
|                             panic!("unexpected write"); | ||||
|                         } | ||||
|                         Async::NotReady => { | ||||
|                             return Err(io::ErrorKind::WouldBlock.into()); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 ret => { | ||||
|                     me.maybe_wakeup_reader(); | ||||
|                     return ret; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     impl AsyncRead for Mock { | ||||
|     } | ||||
|  | ||||
|     impl AsyncWrite for Mock { | ||||
|         fn shutdown(&mut self) -> Poll<(), io::Error> { | ||||
|             Ok(Async::Ready(())) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Returns `true` if called from the context of a futures-rs Task | ||||
|     pub fn is_task_ctx() -> bool { | ||||
|         use std::panic; | ||||
|  | ||||
|         // Save the existing panic hook | ||||
|         let h = panic::take_hook(); | ||||
|  | ||||
|         // Install a new one that does nothing | ||||
|         panic::set_hook(Box::new(|_| {})); | ||||
|  | ||||
|         // Attempt to call the fn | ||||
|         let r = panic::catch_unwind(|| task::current()).is_ok(); | ||||
|  | ||||
|         // Re-install the old one | ||||
|         panic::set_hook(h); | ||||
|  | ||||
|         // Return the result | ||||
|         r | ||||
|     } | ||||
| } | ||||
| @@ -11,7 +11,6 @@ pub extern crate env_logger; | ||||
| pub extern crate futures; | ||||
| pub extern crate h2; | ||||
| pub extern crate http; | ||||
| pub extern crate mock_io; | ||||
| pub extern crate string; | ||||
| pub extern crate tokio_io; | ||||
|  | ||||
| @@ -52,6 +51,7 @@ pub mod raw; | ||||
| pub mod frames; | ||||
| pub mod prelude; | ||||
| pub mod mock; | ||||
| pub mod mock_io; | ||||
| pub mod notify; | ||||
| pub mod util; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user