use std::io::ErrorKind;
use std::sync::Arc;
use std::thread;
use std::time::{Duration, SystemTime};
extern crate posixmq;
use posixmq::{OpenOptions, PosixMq, remove_queue};
fn tmp_mq(name: &str) -> PosixMq {
let mq = OpenOptions::readwrite()
.capacity(1)
.max_msg_len(64)
.create()
.open(name)
.expect(&format!("cannot open or create {}", name));
let _ = remove_queue(name);
mq
}
#[test]
fn timeout_doesnt_block_indefinitely() {
let mq = tmp_mq("/timed_send_and_receive");
let err = mq.recv_timeout(&mut[0; 64], Duration::from_millis(10)).unwrap_err().kind();
assert_eq!(err, ErrorKind::TimedOut);
mq.send_timeout(1, b"1", Duration::from_millis(10)).unwrap();
let err = mq.send_timeout(2, b"2", Duration::from_millis(10)).unwrap_err().kind();
assert_eq!(err, ErrorKind::TimedOut);
mq.recv_timeout(&mut[0; 64], Duration::from_millis(10)).unwrap();
}
#[test]
fn subsecond_timeouts_matter() {
let mq = Arc::new(tmp_mq("/tiny_timeouts_matter"));
let long_mq = mq.clone();
let long = thread::spawn(move|| {
long_mq.recv_timeout(&mut[0; 64], Duration::from_millis(500))
});
let short_mq = mq.clone();
let short = thread::spawn(move|| {
short_mq.recv_timeout(&mut[0; 64], Duration::from_millis(100))
});
thread::sleep(Duration::from_millis(300));
mq.send(0, b"patience is a virtue").unwrap();
assert_eq!(short.join().unwrap().unwrap_err().kind(), ErrorKind::TimedOut);
long.join().unwrap().expect("both sub-second receives failed");
}
#[test]
fn fullsecond_timeouts_matter() {
let mq = Arc::new(tmp_mq("/big_timeouts_matter"));
let long_mq = mq.clone();
let long = thread::spawn(move|| {
long_mq.recv_timeout(&mut[0; 64], Duration::from_secs(2))
});
let short_mq = mq.clone();
let short = thread::spawn(move|| {
short_mq.recv_timeout(&mut[0; 64], Duration::new(1, 100_000_000))
});
thread::sleep(Duration::from_millis(1_550));
mq.send(0, b"patience is a virtue").unwrap();
assert_eq!(short.join().unwrap().unwrap_err().kind(), ErrorKind::TimedOut);
long.join().unwrap().expect("both full-second receives failed");
}
#[test]
fn deadline() {
let mq = tmp_mq("/deadline");
let later = SystemTime::now() + Duration::from_secs(1);
assert_eq!(mq.recv_deadline(&mut[0; 64], later).unwrap_err().kind(), ErrorKind::TimedOut);
assert_eq!(mq.recv_deadline(&mut[0; 64], later).unwrap_err().kind(), ErrorKind::TimedOut);
let later = SystemTime::now() + Duration::from_secs(1);
mq.send_deadline(0, b"I won't wait *forever*", later).unwrap();
let result = mq.send_deadline(0, b"I won't wait *forever*", later);
assert_eq!(result.unwrap_err().kind(), ErrorKind::TimedOut);
}
#[test]
fn expired() {
let mq = tmp_mq("/expired");
let result = mq.recv_timeout(&mut[0; 64], Duration::from_nanos(0));
assert_eq!(result.unwrap_err().kind(), ErrorKind::TimedOut);
let result = mq.recv_deadline(&mut[0; 64], SystemTime::now() - Duration::new(10, 10));
assert_eq!(result.unwrap_err().kind(), ErrorKind::TimedOut);
mq.send_deadline(0, b"no time but space", SystemTime::now()).unwrap();
let result = mq.send_deadline(0, b"no time no space", SystemTime::now());
assert_eq!(result.unwrap_err().kind(), ErrorKind::TimedOut);
}
#[test]
fn ignored_when_nonblocking() {
let mq = OpenOptions::readwrite()
.nonblocking()
.capacity(1)
.max_msg_len(64)
.create()
.open("/deadline_is_now")
.unwrap();
let _ = remove_queue("/deadline_is_now");
let later = SystemTime::now() + Duration::from_secs(60);
let result = mq.recv_deadline(&mut[0; 64], later);
assert_eq!(result.unwrap_err().kind(), ErrorKind::WouldBlock);
mq.send_deadline(0, b"I can't wait", later).unwrap();
let result = mq.send_deadline(0, b"I can't wait", later);
assert_eq!(result.unwrap_err().kind(), ErrorKind::WouldBlock);
}
#[test]
fn bad_timeouts() {
let mq = tmp_mq("/time_overflow");
if cfg!(all(target_os="linux", target_arch="i686")) { let result = mq.send_timeout(0, b"zzz", Duration::new(i32::max_value() as u64, 0));
assert_eq!(result.expect_err("32bit overflow").kind(), ErrorKind::InvalidInput);
}
let result = mq.send_timeout(0, b"zzz", Duration::new(!0, 999_999_999));
assert_eq!(result.expect_err("complete wraparound").kind(), ErrorKind::InvalidInput);
let result = mq.send_timeout(0, b"zzz", Duration::new(!0 - 100, 0));
assert_eq!(result.expect_err("too big").kind(), ErrorKind::InvalidInput);
let result = mq.send_timeout(0, b"zzz", Duration::new(i64::max_value() as u64 - 100, 0));
assert_eq!(result.expect_err("overflow").kind(), ErrorKind::InvalidInput);
}
#[test]
fn negative_deadline() {
let mq = tmp_mq("/negative_deadline");
let error = if cfg!(any(target_os="linux", target_os="dragonfly")) {
ErrorKind::InvalidInput
} else {
ErrorKind::TimedOut
};
let no_fraction = SystemTime::UNIX_EPOCH - Duration::from_secs(365*24*60*60);
let result = mq.recv_deadline(&mut[0; 64], no_fraction);
assert_eq!(result.expect_err("negative deadline").kind(), error);
let only_fraction = SystemTime::UNIX_EPOCH - Duration::new(0, 100_000_000);
let result = mq.recv_deadline(&mut[0; 64], only_fraction);
assert_eq!(result.expect_err("negative deadline").kind(), error);
let with_fraction = SystemTime::UNIX_EPOCH - Duration::new(10, 999_999_999);
let result = mq.recv_deadline(&mut[0; 64], with_fraction);
assert_eq!(result.expect_err("negative deadline").kind(), error);
}