test(h1): fix flaky streaming test
This commit is contained in:
		
							
								
								
									
										62
									
								
								src/mock.rs
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								src/mock.rs
									
									
									
									
									
								
							| @@ -1,7 +1,8 @@ | ||||
| use std::cmp; | ||||
| use std::io::{self, Read, Write}; | ||||
|  | ||||
| use futures::Poll; | ||||
| use bytes::Buf as BufTrait; | ||||
| use futures::{Async, Poll}; | ||||
| use tokio_io::{AsyncRead, AsyncWrite}; | ||||
|  | ||||
| #[derive(Debug)] | ||||
| @@ -11,10 +12,6 @@ pub struct Buf { | ||||
| } | ||||
|  | ||||
| impl Buf { | ||||
|     pub fn new() -> Buf { | ||||
|         Buf::wrap(vec![]) | ||||
|     } | ||||
|  | ||||
|     pub fn wrap(vec: Vec<u8>) -> Buf { | ||||
|         Buf { | ||||
|             vec: vec, | ||||
| @@ -63,23 +60,27 @@ impl Read for Buf { | ||||
|     } | ||||
| } | ||||
|  | ||||
| const READ_VECS_CNT: usize = 64; | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub struct AsyncIo<T> { | ||||
|     inner: T, | ||||
|     blocked: bool, | ||||
|     bytes_until_block: usize, | ||||
|     error: Option<io::Error>, | ||||
|     blocked: bool, | ||||
|     flushed: bool, | ||||
|     inner: T, | ||||
|     max_read_vecs: usize, | ||||
| } | ||||
|  | ||||
| impl<T> AsyncIo<T> { | ||||
|     pub fn new(inner: T, bytes: usize) -> AsyncIo<T> { | ||||
|         AsyncIo { | ||||
|             inner: inner, | ||||
|             blocked: false, | ||||
|             bytes_until_block: bytes, | ||||
|             error: None, | ||||
|             flushed: false, | ||||
|             blocked: false, | ||||
|             inner: inner, | ||||
|             max_read_vecs: READ_VECS_CNT, | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -90,6 +91,22 @@ impl<T> AsyncIo<T> { | ||||
|     pub fn error(&mut self, err: io::Error) { | ||||
|         self.error = Some(err); | ||||
|     } | ||||
|  | ||||
|     pub fn max_read_vecs(&mut self, cnt: usize) { | ||||
|         assert!(cnt <= READ_VECS_CNT); | ||||
|         self.max_read_vecs = cnt; | ||||
|     } | ||||
|  | ||||
|     #[cfg(feature = "tokio-proto")] | ||||
|     //TODO: fix proto::conn::tests to not use tokio-proto API, | ||||
|     //and then this cfg flag go away | ||||
|     pub fn flushed(&self) -> bool { | ||||
|         self.flushed | ||||
|     } | ||||
|  | ||||
|     pub fn blocked(&self) -> bool { | ||||
|         self.blocked | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl AsyncIo<Buf> { | ||||
| @@ -103,16 +120,17 @@ impl AsyncIo<Buf> { | ||||
|     pub fn new_eof() -> AsyncIo<Buf> { | ||||
|         AsyncIo::new(Buf::wrap(Vec::new().into()), 1) | ||||
|     } | ||||
| } | ||||
|  | ||||
|     #[cfg(feature = "tokio-proto")] | ||||
|     //TODO: fix proto::conn::tests to not use tokio-proto API, | ||||
|     //and then this cfg flag go away | ||||
|     pub fn flushed(&self) -> bool { | ||||
|         self.flushed | ||||
|     } | ||||
| impl<T: Read + Write> AsyncIo<T> { | ||||
|     fn write_no_vecs<B: BufTrait>(&mut self, buf: &mut B) -> Poll<usize, io::Error> { | ||||
|         if !buf.has_remaining() { | ||||
|             return Ok(Async::Ready(0)); | ||||
|         } | ||||
|  | ||||
|     pub fn blocked(&self) -> bool { | ||||
|         self.blocked | ||||
|         let n = try_nb!(self.write(buf.bytes())); | ||||
|         buf.advance(n); | ||||
|         Ok(Async::Ready(n)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -170,12 +188,14 @@ impl<T: Read + Write> AsyncWrite for AsyncIo<T> { | ||||
|         Ok(().into()) | ||||
|     } | ||||
|  | ||||
|     fn write_buf<B: ::bytes::Buf>(&mut self, buf: &mut B) -> Poll<usize, io::Error> { | ||||
|         use futures::Async; | ||||
|     fn write_buf<B: BufTrait>(&mut self, buf: &mut B) -> Poll<usize, io::Error> { | ||||
|         if self.max_read_vecs == 0 { | ||||
|             return self.write_no_vecs(buf); | ||||
|         } | ||||
|         let r = { | ||||
|             static DUMMY: &[u8] = &[0]; | ||||
|             let mut bufs = [From::from(DUMMY); 64]; | ||||
|             let i = ::bytes::Buf::bytes_vec(&buf, &mut bufs); | ||||
|             let mut bufs = [From::from(DUMMY); READ_VECS_CNT]; | ||||
|             let i = ::bytes::Buf::bytes_vec(&buf, &mut bufs[..self.max_read_vecs]); | ||||
|             let mut n = 0; | ||||
|             let mut ret = Ok(0); | ||||
|             for iovec in &bufs[..i] { | ||||
|   | ||||
| @@ -440,6 +440,9 @@ impl<T: Buf> Buf for BufDeque<T> { | ||||
|                 return buf.bytes(); | ||||
|             } | ||||
|         } | ||||
|         if let Some(ref buf) = self.bufs.front() { | ||||
|             return buf.bytes(); | ||||
|         } | ||||
|         &[] | ||||
|     } | ||||
|  | ||||
| @@ -483,42 +486,54 @@ impl<T: Buf> Buf for BufDeque<T> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| // TODO: Move tests to their own mod | ||||
| #[cfg(test)] | ||||
| use std::io::Read; | ||||
| mod tests { | ||||
|     use super::*; | ||||
|     use std::io::Read; | ||||
|     use mock::AsyncIo; | ||||
|  | ||||
| #[cfg(test)] | ||||
| impl<T: Read> MemRead for ::mock::AsyncIo<T> { | ||||
|     fn read_mem(&mut self, len: usize) -> Poll<Bytes, io::Error> { | ||||
|         let mut v = vec![0; len]; | ||||
|         let n = try_nb!(self.read(v.as_mut_slice())); | ||||
|         Ok(Async::Ready(BytesMut::from(&v[..n]).freeze())) | ||||
|     #[cfg(test)] | ||||
|     impl<T: Read> MemRead for ::mock::AsyncIo<T> { | ||||
|         fn read_mem(&mut self, len: usize) -> Poll<Bytes, io::Error> { | ||||
|             let mut v = vec![0; len]; | ||||
|             let n = try_nb!(self.read(v.as_mut_slice())); | ||||
|             Ok(Async::Ready(BytesMut::from(&v[..n]).freeze())) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn iobuf_write_empty_slice() { | ||||
|         let mut mock = AsyncIo::new_buf(vec![], 256); | ||||
|         mock.error(io::Error::new(io::ErrorKind::Other, "logic error")); | ||||
|  | ||||
|         let mut io_buf = Buffered::<_, Cursor<Vec<u8>>>::new(mock); | ||||
|  | ||||
|         // underlying io will return the logic error upon write, | ||||
|         // so we are testing that the io_buf does not trigger a write | ||||
|         // when there is nothing to flush | ||||
|         io_buf.flush().expect("should short-circuit flush"); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn parse_reads_until_blocked() { | ||||
|         // missing last line ending | ||||
|         let raw = "HTTP/1.1 200 OK\r\n"; | ||||
|  | ||||
|         let mock = AsyncIo::new_buf(raw, raw.len()); | ||||
|         let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock); | ||||
|         assert_eq!(buffered.parse::<::proto::ClientTransaction>().unwrap(), Async::NotReady); | ||||
|         assert!(buffered.io.blocked()); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn write_buf_skips_empty_bufs() { | ||||
|         let mut mock = AsyncIo::new_buf(vec![], 1024); | ||||
|         mock.max_read_vecs(0); // disable vectored IO | ||||
|         let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock); | ||||
|  | ||||
|         buffered.buffer(Cursor::new(Vec::new())); | ||||
|         buffered.buffer(Cursor::new(b"hello".to_vec())); | ||||
|         buffered.flush().unwrap(); | ||||
|         assert_eq!(buffered.io, b"hello"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn test_iobuf_write_empty_slice() { | ||||
|     use mock::{AsyncIo, Buf as MockBuf}; | ||||
|  | ||||
|     let mut mock = AsyncIo::new(MockBuf::new(), 256); | ||||
|     mock.error(io::Error::new(io::ErrorKind::Other, "logic error")); | ||||
|  | ||||
|     let mut io_buf = Buffered::<_, Cursor<Vec<u8>>>::new(mock); | ||||
|  | ||||
|     // underlying io will return the logic error upon write, | ||||
|     // so we are testing that the io_buf does not trigger a write | ||||
|     // when there is nothing to flush | ||||
|     io_buf.flush().expect("should short-circuit flush"); | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn test_parse_reads_until_blocked() { | ||||
|     use mock::{AsyncIo, Buf as MockBuf}; | ||||
|     // missing last line ending | ||||
|     let raw = "HTTP/1.1 200 OK\r\n"; | ||||
|  | ||||
|     let mock = AsyncIo::new(MockBuf::wrap(raw.into()), raw.len()); | ||||
|     let mut buffered = Buffered::<_, Cursor<Vec<u8>>>::new(mock); | ||||
|     assert_eq!(buffered.parse::<::proto::ClientTransaction>().unwrap(), Async::NotReady); | ||||
|     assert!(buffered.io.blocked()); | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user