blob: d97e6eda2608a24f9faae0a0ee8efc608e2a4c48 [file] [log] [blame]
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);
}