| use std::collections::HashMap; |
| use std::net::{self, Shutdown}; |
| use std::time::{Duration, Instant}; |
| |
| use mio::{Token, Ready, PollOpt, Poll, Events}; |
| use mio::event::{Evented, Event}; |
| use mio::net::TcpStream; |
| |
| struct TestPoll { |
| poll: Poll, |
| events: Events, |
| buf: HashMap<Token, Ready>, |
| } |
| |
| impl TestPoll { |
| fn new() -> TestPoll { |
| TestPoll { |
| poll: Poll::new().unwrap(), |
| events: Events::with_capacity(1024), |
| buf: HashMap::new(), |
| } |
| } |
| |
| fn register<E: ?Sized>(&self, handle: &E, token: Token, interest: Ready, opts: PollOpt) |
| where E: Evented |
| { |
| self.poll.register(handle, token, interest, opts).unwrap(); |
| } |
| |
| fn wait_for(&mut self, token: Token, ready: Ready) -> Result<(), &'static str> { |
| let now = Instant::now(); |
| |
| loop { |
| if now.elapsed() > Duration::from_secs(1) { |
| return Err("not ready"); |
| } |
| |
| if let Some(curr) = self.buf.get(&token) { |
| if curr.contains(ready) { |
| break; |
| } |
| } |
| |
| self.poll.poll(&mut self.events, Some(Duration::from_millis(250))).unwrap(); |
| |
| for event in &self.events { |
| let curr = self.buf.entry(event.token()) |
| .or_insert(Ready::empty()); |
| |
| *curr |= event.readiness(); |
| } |
| } |
| |
| *self.buf.get_mut(&token).unwrap() -= ready; |
| Ok(()) |
| } |
| |
| fn check_idle(&mut self) -> Result<(), Event> { |
| self.poll.poll(&mut self.events, Some(Duration::from_millis(100))).unwrap(); |
| |
| if let Some(e) = self.events.iter().next() { |
| Err(e) |
| } else { |
| Ok(()) |
| } |
| } |
| } |
| |
| macro_rules! assert_ready { |
| ($poll:expr, $token:expr, $ready:expr) => {{ |
| match $poll.wait_for($token, $ready) { |
| Ok(_) => {} |
| Err(_) => panic!("not ready; token = {:?}; interest = {:?}", $token, $ready), |
| } |
| }} |
| } |
| |
| macro_rules! assert_not_ready { |
| ($poll:expr, $token:expr, $ready:expr) => {{ |
| match $poll.wait_for($token, $ready) { |
| Ok(_) => panic!("is ready; token = {:?}; interest = {:?}", $token, $ready), |
| Err(_) => {} |
| } |
| }} |
| } |
| |
| macro_rules! assert_hup_ready { |
| ($poll:expr) => { |
| #[cfg(unix)] |
| { |
| use mio::unix::UnixReady; |
| assert_ready!($poll, Token(0), Ready::from(UnixReady::hup())) |
| } |
| } |
| } |
| |
| macro_rules! assert_not_hup_ready { |
| ($poll:expr) => { |
| #[cfg(unix)] |
| { |
| use mio::unix::UnixReady; |
| assert_not_ready!($poll, Token(0), Ready::from(UnixReady::hup())) |
| } |
| } |
| } |
| |
| macro_rules! assert_idle { |
| ($poll:expr) => { |
| match $poll.check_idle() { |
| Ok(()) => {} |
| Err(e) => panic!("not idle; event = {:?}", e), |
| } |
| } |
| } |
| |
| // TODO: replace w/ assertive |
| // https://github.com/carllerche/assertive |
| macro_rules! assert_ok { |
| ($e:expr) => { |
| assert_ok!($e,) |
| }; |
| ($e:expr,) => {{ |
| use std::result::Result::*; |
| match $e { |
| Ok(v) => v, |
| Err(e) => panic!("assertion failed: error = {:?}", e), |
| } |
| }}; |
| ($e:expr, $($arg:tt)+) => {{ |
| use std::result::Result::*; |
| match $e { |
| Ok(v) => v, |
| Err(e) => panic!("assertion failed: error = {:?}: {}", e, format_args!($($arg)+)), |
| } |
| }}; |
| } |
| |
| #[test] |
| fn test_write_shutdown() { |
| use std::io::prelude::*; |
| |
| let mut poll = TestPoll::new(); |
| let mut buf = [0; 1024]; |
| |
| let listener = assert_ok!(net::TcpListener::bind("127.0.0.1:0")); |
| let addr = assert_ok!(listener.local_addr()); |
| |
| let mut client = assert_ok!(TcpStream::connect(&addr)); |
| poll.register(&client, |
| Token(0), |
| Ready::readable() | Ready::writable(), |
| PollOpt::edge()); |
| |
| let (socket, _) = assert_ok!(listener.accept()); |
| |
| assert_ready!(poll, Token(0), Ready::writable()); |
| |
| // Polling should not have any events |
| assert_idle!(poll); |
| |
| // Now, shutdown the write half of the socket. |
| assert_ok!(socket.shutdown(Shutdown::Write)); |
| |
| assert_ready!(poll, Token(0), Ready::readable()); |
| |
| assert_not_hup_ready!(poll); |
| |
| let n = assert_ok!(client.read(&mut buf)); |
| assert_eq!(n, 0); |
| } |
| |
| #[test] |
| fn test_graceful_shutdown() { |
| use std::io::prelude::*; |
| |
| let mut poll = TestPoll::new(); |
| let mut buf = [0; 1024]; |
| |
| let listener = assert_ok!(net::TcpListener::bind("127.0.0.1:0")); |
| let addr = assert_ok!(listener.local_addr()); |
| |
| let mut client = assert_ok!(TcpStream::connect(&addr)); |
| poll.register(&client, |
| Token(0), |
| Ready::readable() | Ready::writable(), |
| PollOpt::edge()); |
| |
| let (mut socket, _) = assert_ok!(listener.accept()); |
| |
| assert_ready!(poll, Token(0), Ready::writable()); |
| |
| // Polling should not have any events |
| assert_idle!(poll); |
| |
| // Now, shutdown the write half of the socket. |
| assert_ok!(client.shutdown(Shutdown::Write)); |
| |
| let n = assert_ok!(socket.read(&mut buf)); |
| assert_eq!(0, n); |
| drop(socket); |
| |
| assert_ready!(poll, Token(0), Ready::readable()); |
| #[cfg(not(any( |
| target_os = "bitrig", |
| target_os = "dragonfly", |
| target_os = "freebsd", |
| target_os = "illumos", |
| target_os = "ios", |
| target_os = "macos", |
| target_os = "netbsd", |
| target_os = "openbsd" |
| )))] |
| assert_hup_ready!(poll); |
| |
| let mut buf = [0; 1024]; |
| let n = assert_ok!(client.read(&mut buf)); |
| assert_eq!(n, 0); |
| } |
| |
| #[test] |
| fn test_abrupt_shutdown() { |
| use net2::TcpStreamExt; |
| use std::io::Read; |
| |
| let mut poll = TestPoll::new(); |
| let mut buf = [0; 1024]; |
| |
| let listener = assert_ok!(net::TcpListener::bind("127.0.0.1:0")); |
| let addr = assert_ok!(listener.local_addr()); |
| |
| let mut client = assert_ok!(TcpStream::connect(&addr)); |
| poll.register(&client, |
| Token(0), |
| Ready::readable() | Ready::writable(), |
| PollOpt::edge()); |
| |
| let (socket, _) = assert_ok!(listener.accept()); |
| assert_ok!(socket.set_linger(Some(Duration::from_millis(0)))); |
| // assert_ok!(socket.set_linger(None)); |
| |
| // Wait to be connected |
| assert_ready!(poll, Token(0), Ready::writable()); |
| |
| drop(socket); |
| |
| #[cfg(not(any( |
| target_os = "bitrig", |
| target_os = "dragonfly", |
| target_os = "freebsd", |
| target_os = "illumos", |
| target_os = "ios", |
| target_os = "macos", |
| target_os = "netbsd", |
| target_os = "openbsd" |
| )))] |
| assert_hup_ready!(poll); |
| assert_ready!(poll, Token(0), Ready::writable()); |
| assert_ready!(poll, Token(0), Ready::readable()); |
| |
| let res = client.read(&mut buf); |
| assert!(res.is_err(), "not err = {:?}", res); |
| } |