use super::{Interest, ReadinessPoll};
use std::io;
use std::os::fd::{AsRawFd, RawFd};
use std::time::Duration;
pub struct OwnedSocket(RawFd);
impl AsRawFd for OwnedSocket {
fn as_raw_fd(&self) -> RawFd {
self.0
}
}
impl Drop for OwnedSocket {
fn drop(&mut self) {
let _ = syscall!(close(self.0));
}
}
pub fn create_socket_pair() -> io::Result<(OwnedSocket, OwnedSocket)> {
let mut fds = [0i32; 2];
let _ = syscall!(socketpair(
libc::AF_UNIX,
libc::SOCK_STREAM,
0,
fds.as_mut_ptr()
))?;
Ok((OwnedSocket(fds[0]), OwnedSocket(fds[1])))
}
pub fn make_nonblocking(fd: RawFd) -> io::Result<()> {
let flags = syscall!(fcntl(fd, libc::F_GETFL))?;
syscall!(fcntl(fd, libc::F_SETFL, flags | libc::O_NONBLOCK))?;
Ok(())
}
pub fn test_add_read_no_data<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, _sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
make_nonblocking(fd1)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
poller.add(fd1, 1, Interest::READ)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(10)))?;
assert_eq!(n, 0, "Expected no events when no data is available");
Ok(())
}
pub fn test_read_becomes_ready<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
let fd2 = sock2.as_raw_fd();
make_nonblocking(fd1)?;
make_nonblocking(fd2)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
let data = b"hello";
let written =
syscall!(write(fd2, data.as_ptr() as *const libc::c_void, data.len()))?;
assert!(written > 0, "Failed to write data");
poller.add(fd1, 1, Interest::READ)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert_eq!(n, 1, "Expected 1 read event");
let key = P::event_key(&events[0]);
let interest = P::event_interest(&events[0]);
assert_eq!(key, 1);
assert!(interest.is_readable(), "Event should be readable");
Ok(())
}
pub fn test_write_immediately_ready<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, _sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
make_nonblocking(fd1)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
poller.add(fd1, 2, Interest::WRITE)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert!(n >= 1, "Expected at least 1 write event");
let mut found_write = false;
for i in 0..n {
let key = P::event_key(&events[i]);
let interest = P::event_interest(&events[i]);
if key == 2 && interest.is_writable() {
found_write = true;
break;
}
}
assert!(found_write, "Expected writable event with key 2");
Ok(())
}
pub fn test_add_both_interests<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, _sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
make_nonblocking(fd1)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
let both = Interest::READ_AND_WRITE;
poller.add(fd1, 3, both)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert!(n >= 1, "Expected at least 1 event");
let mut found_event = false;
for i in 0..n {
let key = P::event_key(&events[i]);
if key == 3 {
found_event = true;
break;
}
}
assert!(found_event, "Expected event with key 3");
Ok(())
}
pub fn test_modify_interest<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, _sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
make_nonblocking(fd1)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
poller.add(fd1, 4, Interest::READ)?;
poller.modify(fd1, 4, Interest::WRITE)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert!(n >= 1, "Expected at least 1 event after modify");
Ok(())
}
pub fn test_delete_interest<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, _sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
make_nonblocking(fd1)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
poller.add(fd1, 5, Interest::WRITE)?;
poller.delete(fd1)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(10)))?;
for i in 0..n {
let key = P::event_key(&events[i]);
assert_ne!(key, 5, "Should not get events after delete");
}
Ok(())
}
pub fn test_multiple_fds<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, _) = create_socket_pair()?;
let (sock2, _) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
let fd2 = sock2.as_raw_fd();
make_nonblocking(fd1)?;
make_nonblocking(fd2)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
poller.add(fd1, 10, Interest::WRITE)?;
poller.add(fd2, 20, Interest::WRITE)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert!(n >= 2, "Expected at least 2 events for 2 fds, got {}", n);
let mut found_fd1 = false;
let mut found_fd2 = false;
for i in 0..n {
let key = P::event_key(&events[i]);
if key == 10 {
found_fd1 = true;
}
if key == 20 {
found_fd2 = true;
}
}
assert!(found_fd1, "Expected event for fd1 with key 10");
assert!(found_fd2, "Expected event for fd2 with key 20");
Ok(())
}
pub fn test_notify_works<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
poller.notify()?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
let start = std::time::Instant::now();
let _n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
let elapsed = start.elapsed();
assert!(
elapsed < Duration::from_millis(200),
"Wait should return promptly, but took {:?}",
elapsed
);
Ok(())
}
pub fn test_delete_nonexistent_fd<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let result = poller.delete(999);
assert!(result.is_err(), "Deleting non-existent fd should return error");
assert_eq!(
result.unwrap_err().raw_os_error(),
Some(libc::ENOENT),
"Error should be ENOENT"
);
Ok(())
}
pub fn test_reregister_same_fd<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, _sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
make_nonblocking(fd1)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
poller.add(fd1, 1, Interest::WRITE)?;
poller.modify(fd1, 2, Interest::WRITE)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert!(n >= 1, "Should get at least one event");
Ok(())
}
pub fn test_timeout_no_events<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, _sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
make_nonblocking(fd1)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
poller.add(fd1, 1, Interest::READ)?;
let start = std::time::Instant::now();
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
let elapsed = start.elapsed();
assert_eq!(n, 0, "Should get no events");
assert!(
elapsed >= Duration::from_millis(90),
"Should wait close to timeout duration"
);
assert!(elapsed < Duration::from_millis(200), "Should not wait too long");
Ok(())
}
pub fn test_zero_timeout<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, _sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
make_nonblocking(fd1)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
poller.add(fd1, 1, Interest::READ)?;
let start = std::time::Instant::now();
let _n = poller.wait(&mut events, Some(Duration::from_millis(0)))?;
let elapsed = start.elapsed();
assert!(
elapsed < Duration::from_millis(50),
"Zero timeout should return immediately"
);
Ok(())
}
pub fn test_many_fds<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let mut events = vec![unsafe { std::mem::zeroed() }; 128];
let mut sockets = Vec::new();
for i in 0..20 {
let (sock, _) = create_socket_pair()?;
let fd = sock.as_raw_fd();
make_nonblocking(fd)?;
poller.add(fd, i, Interest::WRITE)?;
sockets.push(sock);
}
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert!(n >= 20, "Should get events for most/all fds, got {}", n);
Ok(())
}
pub fn test_partial_read<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
let fd2 = sock2.as_raw_fd();
make_nonblocking(fd1)?;
make_nonblocking(fd2)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
let data = b"hello world";
let _ =
syscall!(write(fd2, data.as_ptr() as *const libc::c_void, data.len()));
poller.add(fd1, 1, Interest::READ)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert_eq!(n, 1, "Should get read event");
let mut buf = [0u8; 5];
let ret =
syscall!(read(fd1, buf.as_mut_ptr() as *mut libc::c_void, buf.len()))?;
assert_eq!(ret, 5, "Should read 5 bytes");
poller.modify(fd1, 2, Interest::READ)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert_eq!(n, 1, "Should still have more data to read");
Ok(())
}
pub fn test_rapid_add_delete<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, _sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
make_nonblocking(fd1)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
for i in 0..10 {
poller.add(fd1, i, Interest::WRITE)?;
poller.delete(fd1)?;
}
poller.add(fd1, 100, Interest::WRITE)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert!(n >= 1, "Should get event after rapid add/delete cycles");
Ok(())
}
pub fn test_modify_read_to_write<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
let fd2 = sock2.as_raw_fd();
make_nonblocking(fd1)?;
make_nonblocking(fd2)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
poller.add(fd1, 1, Interest::READ)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(10)))?;
assert_eq!(n, 0, "Should have no read events");
poller.modify(fd1, 1, Interest::WRITE)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert!(n >= 1, "Should get write event");
let key = P::event_key(&events[0]);
let interest = P::event_interest(&events[0]);
assert_eq!(key, 1);
assert!(interest.is_writable(), "Event should be writable");
Ok(())
}
pub fn test_close_registered_fd<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, _sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
make_nonblocking(fd1)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
poller.add(fd1, 1, Interest::WRITE)?;
let _ = syscall!(close(fd1));
std::mem::forget(sock1);
let _n = poller.wait(&mut events, Some(Duration::from_millis(10)))?;
Ok(())
}
pub fn test_multiple_notifies<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
poller.notify()?;
poller.notify()?;
poller.notify()?;
let start = std::time::Instant::now();
let _n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
let elapsed = start.elapsed();
assert!(
elapsed < Duration::from_millis(200),
"Multiple notifies should still work correctly"
);
Ok(())
}
pub fn test_modify_write_to_read<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
let fd2 = sock2.as_raw_fd();
make_nonblocking(fd1)?;
make_nonblocking(fd2)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
poller.add(fd1, 1, Interest::WRITE)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert!(n >= 1, "Should get write event");
poller.modify(fd1, 1, Interest::READ)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(10)))?;
assert_eq!(n, 0, "Should have no read events without data");
let data = b"test";
let _ =
syscall!(write(fd2, data.as_ptr() as *const libc::c_void, data.len()));
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert_eq!(n, 1, "Should get read event after data written");
Ok(())
}
pub fn test_readd_after_delete<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, _sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
make_nonblocking(fd1)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
poller.add(fd1, 100, Interest::WRITE)?;
poller.delete(fd1)?;
poller.add(fd1, 200, Interest::WRITE)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert!(n >= 1, "Should get event after re-adding");
let key = P::event_key(&events[0]);
assert_eq!(
key, 200,
"Should get event with new key (200), not old key (100)"
);
Ok(())
}
pub fn test_simultaneous_read_write<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
let fd2 = sock2.as_raw_fd();
make_nonblocking(fd1)?;
make_nonblocking(fd2)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
let data = b"hello";
let _ =
syscall!(write(fd2, data.as_ptr() as *const libc::c_void, data.len()));
let both = Interest::READ_AND_WRITE;
poller.add(fd1, 42, both)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert!(n >= 1, "Should get at least one event");
let interest = P::event_interest(&events[0]);
assert!(
interest.is_readable() || interest.is_writable(),
"Should have at least one interest"
);
Ok(())
}
pub fn test_peer_closed<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
let fd2 = sock2.as_raw_fd();
make_nonblocking(fd1)?;
make_nonblocking(fd2)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
poller.add(fd1, 1, Interest::READ)?;
let _ = syscall!(close(fd2));
std::mem::forget(sock2);
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert!(n >= 1, "Should get event when peer closes");
let interest = P::event_interest(&events[0]);
assert!(
interest.is_readable() || interest.is_writable(),
"Should get some interest signaled on peer close"
);
Ok(())
}
pub fn test_modify_to_no_interest<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, _sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
make_nonblocking(fd1)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
poller.add(fd1, 1, Interest::WRITE)?;
let none = Interest::NONE;
poller.modify(fd1, 1, none)?;
let _n = poller.wait(&mut events, Some(Duration::from_millis(10)))?;
Ok(())
}
pub fn test_buffer_smaller_than_ready_events<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let mut sockets = Vec::new();
for i in 0..10 {
let (sock, _) = create_socket_pair()?;
let fd = sock.as_raw_fd();
make_nonblocking(fd)?;
poller.add(fd, i as u64, Interest::WRITE)?;
sockets.push(sock);
}
let mut events = vec![unsafe { std::mem::zeroed() }; 3];
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert_eq!(n, 3, "Should return buffer size when more events are ready");
for i in 0..n {
let key = P::event_key(&events[i]);
let interest = P::event_interest(&events[i]);
assert!(key < 10, "Key should be in valid range");
assert!(interest.is_writable(), "Event should be writable");
}
Ok(())
}
pub fn test_oneshot_no_redelivery<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
let fd2 = sock2.as_raw_fd();
make_nonblocking(fd1)?;
make_nonblocking(fd2)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
let data = b"hello";
let _ =
syscall!(write(fd2, data.as_ptr() as *const libc::c_void, data.len()));
poller.add(fd1, 1, Interest::READ)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert_eq!(n, 1, "Should get 1 read event on first wait");
assert_eq!(P::event_key(&events[0]), 1);
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert_eq!(n, 0, "ONESHOT: should not re-deliver event without re-arm");
poller.modify(fd1, 1, Interest::READ)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert_eq!(n, 1, "Should get event after re-arm");
Ok(())
}
pub fn test_wait_infinite_timeout<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, _sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
make_nonblocking(fd1)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
poller.add(fd1, 1, Interest::WRITE)?;
let start = std::time::Instant::now();
let n = poller.wait(&mut events, None)?;
let elapsed = start.elapsed();
assert_eq!(n, 1, "Should get 1 write event");
assert!(
elapsed < Duration::from_millis(100),
"Should return quickly when event is ready, even with infinite timeout"
);
Ok(())
}
pub fn test_add_duplicate_fd<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, _sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
make_nonblocking(fd1)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
poller.add(fd1, 1, Interest::READ)?;
let result = poller.add(fd1, 2, Interest::WRITE);
if result.is_ok() {
let n = poller.wait(&mut events, Some(Duration::from_millis(10)))?;
assert!(n <= 1, "Should not get duplicate events for same fd");
if n == 1 {
let key = P::event_key(&events[0]);
assert!(
key == 1 || key == 2,
"Key should match one of the registrations"
);
}
}
Ok(())
}
pub fn test_same_key_different_fds<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, _) = create_socket_pair()?;
let (sock2, _) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
let fd2 = sock2.as_raw_fd();
make_nonblocking(fd1)?;
make_nonblocking(fd2)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
poller.add(fd1, 42, Interest::WRITE)?;
poller.add(fd2, 42, Interest::WRITE)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert_eq!(n, 2, "Should get events for both fds despite key collision");
for i in 0..n {
let key = P::event_key(&events[i]);
assert_eq!(key, 42, "All events should have the same key");
let interest = P::event_interest(&events[i]);
assert!(interest.is_writable(), "Events should be writable");
}
Ok(())
}
pub fn test_wait_empty_buffer<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, _sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
make_nonblocking(fd1)?;
poller.add(fd1, 1, Interest::WRITE)?;
let mut events = [];
let result = poller.wait(&mut events, Some(Duration::from_millis(10)));
match result {
Ok(n) => {
assert_eq!(n, 0, "Empty buffer should return 0 events");
}
Err(e) => {
assert_eq!(
e.raw_os_error(),
Some(libc::EINVAL),
"Empty buffer should either succeed with 0 or fail with EINVAL"
);
}
}
Ok(())
}
pub fn test_add_invalid_fd<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let result = poller.add(-1, 1, Interest::READ);
match result {
Ok(_) => {
}
Err(e) => {
assert!(e.raw_os_error().is_some(), "Should get OS error for invalid fd");
}
}
Ok(())
}
pub fn test_add_closed_fd<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, _sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
unsafe {
libc::close(fd1);
}
std::mem::forget(sock1);
let result = poller.add(fd1, 1, Interest::READ);
if result.is_err() {
}
Ok(())
}
pub fn test_edge_key_values<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, _) = create_socket_pair()?;
let (sock2, _) = create_socket_pair()?;
let (sock3, _) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
let fd2 = sock2.as_raw_fd();
let fd3 = sock3.as_raw_fd();
make_nonblocking(fd1)?;
make_nonblocking(fd2)?;
make_nonblocking(fd3)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
poller.add(fd1, 0, Interest::WRITE)?;
poller.add(fd2, u64::MAX, Interest::WRITE)?;
poller.add(fd3, u64::MAX - 1, Interest::WRITE)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert!(n >= 2, "Should get at least 2 events for edge keys");
assert!(n <= 3, "Should get at most 3 events");
let mut found_zero = false;
let mut _found_max = false;
let mut _found_max_minus_1 = false;
for i in 0..n {
let key = P::event_key(&events[i]);
if key == 0 {
found_zero = true;
}
if key == u64::MAX {
_found_max = true;
}
if key == u64::MAX - 1 {
_found_max_minus_1 = true;
}
}
assert!(found_zero, "Should find event with key 0");
Ok(())
}
pub fn test_read_interest_filtering<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
let fd2 = sock2.as_raw_fd();
make_nonblocking(fd1)?;
make_nonblocking(fd2)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
poller.add(fd1, 1, Interest::READ)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(10)))?;
assert_eq!(n, 0, "READ interest should not report write-only readiness");
let data = b"test";
let _ = unsafe {
libc::write(fd2, data.as_ptr() as *const libc::c_void, data.len())
};
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert!(n > 0, "Should get read event when data is available");
let mut found_readable = false;
for i in 0..n {
let event = &events[i];
let key = P::event_key(event);
let interest = P::event_interest(event);
if key == 1 {
assert!(interest.is_readable(), "Event should be readable");
found_readable = true;
}
}
assert!(found_readable, "Should find readable event");
Ok(())
}
pub fn test_write_interest_filtering<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
let fd2 = sock2.as_raw_fd();
make_nonblocking(fd1)?;
make_nonblocking(fd2)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
poller.add(fd1, 1, Interest::WRITE)?;
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert!(n > 0, "Should get write event when socket is writable");
let mut found_writable = false;
for i in 0..n {
let event = &events[i];
let key = P::event_key(event);
let interest = P::event_interest(event);
if key == 1 {
assert!(interest.is_writable(), "Event should be writable");
found_writable = true;
}
}
assert!(found_writable, "Should find writable event");
poller.modify(fd1, 1, Interest::WRITE)?;
let data = b"test";
let _ = unsafe {
libc::write(fd2, data.as_ptr() as *const libc::c_void, data.len())
};
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert!(n > 0, "Should get write event");
let mut found_writable_only = false;
for i in 0..n {
let event = &events[i];
let key = P::event_key(event);
let interest = P::event_interest(event);
if key == 1 {
assert!(interest.is_writable(), "Event should be writable");
found_writable_only = true;
}
}
assert!(found_writable_only, "Should find writable event");
Ok(())
}
pub fn test_modify_nonexistent_fd<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, _sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
make_nonblocking(fd1)?;
let result = poller.modify(fd1, 1, Interest::READ);
assert!(result.is_err(), "Modifying non-existent fd should return error");
Ok(())
}
pub fn test_fd_reuse_after_delete<P>(poller: P) -> io::Result<()>
where
P: ReadinessPoll,
P::NativeEvent: Clone,
{
let (sock1, sock2) = create_socket_pair()?;
let fd1 = sock1.as_raw_fd();
let fd2 = sock2.as_raw_fd();
make_nonblocking(fd1)?;
make_nonblocking(fd2)?;
let mut events = vec![unsafe { std::mem::zeroed() }; 16];
poller.add(fd1, 1, Interest::READ)?;
let data = b"test";
let _ = unsafe {
libc::write(fd2, data.as_ptr() as *const libc::c_void, data.len())
};
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
assert!(n > 0, "Should get read event");
poller.delete(fd1)?;
drop(sock1);
drop(sock2);
let (sock3, sock4) = create_socket_pair()?;
let fd3 = sock3.as_raw_fd();
let fd4 = sock4.as_raw_fd();
make_nonblocking(fd3)?;
make_nonblocking(fd4)?;
poller.add(fd3, 2, Interest::READ)?;
let _ = unsafe {
libc::write(fd4, data.as_ptr() as *const libc::c_void, data.len())
};
let n = poller.wait(&mut events, Some(Duration::from_millis(100)))?;
if n > 0 {
for i in 0..n {
let event = &events[i];
let key = P::event_key(event);
assert_eq!(
key, 2,
"Event should have new key 2, not old key 1 - fd state not properly cleaned"
);
}
}
Ok(())
}
#[macro_export]
macro_rules! generate_tests {
($poller:expr) => {
#[test]
fn test_add_read_no_data() {
println!("Running test: add read interest with no data available");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_add_read_no_data(poller)
.expect("test_add_read_no_data: failed to add read interest with no data");
}
#[test]
fn test_read_becomes_ready() {
println!("Running test: read interest triggers when data is written");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_read_becomes_ready(poller)
.expect("test_read_becomes_ready: failed when testing read interest triggers on data write");
}
#[test]
fn test_write_immediately_ready() {
println!("Running test: write interest triggers immediately");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_write_immediately_ready(poller)
.expect("test_write_immediately_ready: failed when testing immediate write readiness");
}
#[test]
fn test_add_both_interests() {
println!("Running test: adding both read and write interests simultaneously");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_add_both_interests(poller)
.expect("test_add_both_interests: failed when adding both read and write interests");
}
#[test]
fn test_modify_interest() {
println!("Running test: modifying interest on existing fd");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_modify_interest(poller)
.expect("test_modify_interest: failed when modifying interest on existing fd");
}
#[test]
fn test_delete_interest() {
println!("Running test: deleting interest prevents further events");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_delete_interest(poller)
.expect("test_delete_interest: failed when testing delete prevents events");
}
#[test]
fn test_multiple_fds() {
println!("Running test: monitoring multiple file descriptors simultaneously");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_multiple_fds(poller)
.expect("test_multiple_fds: failed when monitoring multiple file descriptors");
}
#[test]
fn test_notify_works() {
println!("Running test: notify() can be called without error");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_notify_works(poller)
.expect("test_notify_works: failed when testing notify()");
}
#[test]
fn test_delete_nonexistent_fd() {
println!("Running test: deleting non-existent fd returns ENOENT");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_delete_nonexistent_fd(poller)
.expect("test_delete_nonexistent_fd: failed when testing deletion of non-existent fd");
}
#[test]
fn test_reregister_same_fd() {
println!("Running test: modifying same fd with different keys");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_reregister_same_fd(poller)
.expect("test_reregister_same_fd: failed when modifying same fd with different keys");
}
#[test]
fn test_timeout_no_events() {
println!("Running test: timeout works correctly when no events are ready");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_timeout_no_events(poller)
.expect("test_timeout_no_events: failed when testing timeout with no events");
}
#[test]
fn test_zero_timeout() {
println!("Running test: zero timeout returns immediately");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_zero_timeout(poller)
.expect("test_zero_timeout: failed when testing zero timeout immediate return");
}
#[test]
fn test_many_fds() {
println!("Running test: handling many file descriptors");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_many_fds(poller)
.expect("test_many_fds: failed when handling many file descriptors");
}
#[test]
fn test_partial_read() {
println!("Running test: reads work with partial data");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_partial_read(poller)
.expect("test_partial_read: failed when testing partial read handling");
}
#[test]
fn test_rapid_add_delete() {
println!("Running test: rapid add/delete cycles");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_rapid_add_delete(poller)
.expect("test_rapid_add_delete: failed when testing rapid add/delete cycles");
}
#[test]
fn test_modify_read_to_write() {
println!("Running test: modifying from read to write interest");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_modify_read_to_write(poller)
.expect("test_modify_read_to_write: failed when modifying from read to write interest");
}
#[test]
fn test_close_registered_fd() {
println!("Running test: closing a registered fd doesn't crash");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_close_registered_fd(poller)
.expect("test_close_registered_fd: failed when testing closing registered fd");
}
#[test]
fn test_multiple_notifies() {
println!("Running test: multiple notifies in quick succession");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_multiple_notifies(poller)
.expect("test_multiple_notifies: failed when testing multiple notifies in succession");
}
#[test]
fn test_modify_write_to_read() {
println!("Running test: modifying from write to read interest");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_modify_write_to_read(poller)
.expect("test_modify_write_to_read: failed when modifying from write to read interest");
}
#[test]
fn test_readd_after_delete() {
println!("Running test: re-adding file descriptor after deletion");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_readd_after_delete(poller)
.expect("test_readd_after_delete: failed when re-adding fd after deletion");
}
#[test]
fn test_simultaneous_read_write() {
println!("Running test: simultaneous read and write events on same fd");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_simultaneous_read_write(poller)
.expect("test_simultaneous_read_write: failed when testing simultaneous read/write events");
}
#[test]
fn test_peer_closed() {
println!("Running test: handling socket peer close (HUP/ERR conditions)");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_peer_closed(poller)
.expect("test_peer_closed: failed when testing socket peer close handling");
}
#[test]
fn test_modify_to_no_interest() {
println!("Running test: modifying interest to none (edge case)");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_modify_to_no_interest(poller)
.expect("test_modify_to_no_interest: failed when modifying interest to none");
}
#[test]
fn test_buffer_smaller_than_ready_events() {
println!("Running test: buffer too small for all ready events");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_buffer_smaller_than_ready_events(poller)
.expect("test_buffer_smaller_than_ready_events: failed when testing small buffer with many ready events");
}
#[test]
fn test_oneshot_no_redelivery() {
println!("Running test: ONESHOT events should not re-deliver without re-arm");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_oneshot_no_redelivery(poller)
.expect("test_oneshot_no_redelivery: failed when verifying ONESHOT semantics");
}
#[test]
fn test_wait_infinite_timeout() {
println!("Running test: infinite timeout (None) should wait indefinitely");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_wait_infinite_timeout(poller)
.expect("test_wait_infinite_timeout: failed when testing None timeout");
}
#[test]
fn test_add_duplicate_fd() {
println!("Running test: adding already-registered fd");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_add_duplicate_fd(poller)
.expect("test_add_duplicate_fd: failed when testing duplicate fd registration");
}
#[test]
fn test_same_key_different_fds() {
println!("Running test: same key used for different fds");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_same_key_different_fds(poller)
.expect("test_same_key_different_fds: failed when testing key collision");
}
#[test]
fn test_wait_empty_buffer() {
println!("Running test: wait with empty event buffer");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_wait_empty_buffer(poller)
.expect("test_wait_empty_buffer: failed when testing empty buffer");
}
#[test]
fn test_add_invalid_fd() {
println!("Running test: adding invalid file descriptor (-1)");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_add_invalid_fd(poller)
.expect("test_add_invalid_fd: failed when testing invalid fd");
}
#[test]
fn test_add_closed_fd() {
println!("Running test: adding already-closed file descriptor");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_add_closed_fd(poller)
.expect("test_add_closed_fd: failed when testing closed fd");
}
#[test]
fn test_edge_key_values() {
println!("Running test: edge key values (0, MAX, MAX-1)");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_edge_key_values(poller)
.expect("test_edge_key_values: failed when testing edge key values");
}
#[test]
fn test_read_interest_filtering() {
println!("Running test: READ interest filtering");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_read_interest_filtering(poller)
.expect("test_read_interest_filtering: failed when testing READ interest filtering");
}
#[test]
fn test_write_interest_filtering() {
println!("Running test: WRITE interest filtering");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_write_interest_filtering(poller)
.expect("test_write_interest_filtering: failed when testing WRITE interest filtering");
}
#[test]
fn test_modify_nonexistent_fd() {
println!("Running test: modifying non-existent fd");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_modify_nonexistent_fd(poller)
.expect("test_modify_nonexistent_fd: failed when testing modify on non-existent fd");
}
#[test]
fn test_fd_reuse_after_delete() {
println!("Running test: fd reuse after delete");
let poller = $poller;
crate::backends::impls::pollingv2::tests::test_fd_reuse_after_delete(poller)
.expect("test_fd_reuse_after_delete: failed when testing fd reuse after delete");
}
};
}