use yip_io::af_xdp::{
RxRing, TxRing, UmemPool, XskBindMode, XskDesc, XskSocket, UMEM_CHUNK_SIZE, UMEM_RING_SIZE,
};
#[test]
fn test_xsk_bind_mode_variants_and_traits() {
let mode_zc = XskBindMode::ZeroCopy;
let mode_cp = XskBindMode::Copy;
let mode_fb = XskBindMode::FallbackRecvmmsg;
assert_ne!(mode_zc, mode_cp);
assert_ne!(mode_cp, mode_fb);
assert_ne!(mode_zc, mode_fb);
let copied = mode_fb;
assert_eq!(copied, XskBindMode::FallbackRecvmmsg);
assert_eq!(format!("{:?}", mode_zc), "ZeroCopy");
}
#[test]
fn test_xsk_desc_fields_and_traits() {
let desc = XskDesc {
addr: 4096,
len: 1500,
flags: 0,
};
assert_eq!(desc.addr, 4096);
assert_eq!(desc.len, 1500);
assert_eq!(desc.flags, 0);
let desc2 = desc;
assert_eq!(desc, desc2);
assert_eq!(
format!("{:?}", desc),
"XskDesc { addr: 4096, len: 1500, flags: 0 }"
);
}
#[test]
fn test_rx_ring_operations_and_batching() {
let mut ring = RxRing::new(64);
assert_eq!(ring.capacity(), 64);
assert_eq!(ring.len(), 0);
assert!(ring.is_empty());
assert!(!ring.is_full());
let desc1 = XskDesc {
addr: 2048,
len: 512,
flags: 0,
};
assert!(ring.produce(desc1));
assert_eq!(ring.len(), 1);
assert!(!ring.is_empty());
assert_eq!(ring.consume(), Some(desc1));
assert_eq!(ring.len(), 0);
assert!(ring.is_empty());
assert_eq!(ring.consume(), None);
let batch = [
XskDesc {
addr: 0,
len: 100,
flags: 0,
},
XskDesc {
addr: 2048,
len: 200,
flags: 1,
},
XskDesc {
addr: 4096,
len: 300,
flags: 2,
},
];
let produced = ring.produce_batch(&batch);
assert_eq!(produced, 3);
assert_eq!(ring.len(), 3);
let mut out = [XskDesc {
addr: 0,
len: 0,
flags: 0,
}; 4];
let consumed = ring.consume_batch(&mut out);
assert_eq!(consumed, 3);
assert_eq!(&out[..3], &batch);
assert!(ring.is_empty());
}
#[test]
fn test_tx_ring_operations_and_batching() {
let mut ring = TxRing::new(4);
assert_eq!(ring.capacity(), 4);
let d0 = XskDesc {
addr: 0,
len: 64,
flags: 0,
};
let d1 = XskDesc {
addr: 2048,
len: 128,
flags: 0,
};
let d2 = XskDesc {
addr: 4096,
len: 256,
flags: 0,
};
let d3 = XskDesc {
addr: 6144,
len: 512,
flags: 0,
};
let d4 = XskDesc {
addr: 8192,
len: 1024,
flags: 0,
};
assert!(ring.produce(d0));
assert!(ring.produce(d1));
assert!(ring.produce(d2));
assert!(ring.produce(d3));
assert!(ring.is_full());
assert!(!ring.produce(d4));
assert_eq!(ring.consume(), Some(d0));
assert!(!ring.is_full());
assert!(ring.produce(d4));
let mut out = [XskDesc {
addr: 0,
len: 0,
flags: 0,
}; 4];
let consumed = ring.consume_batch(&mut out);
assert_eq!(consumed, 4);
assert_eq!(&out, &[d1, d2, d3, d4]);
assert!(ring.is_empty());
}
#[test]
#[should_panic(expected = "power of two")]
fn test_rx_ring_non_power_of_two_panics() {
let _ = RxRing::new(3);
}
#[test]
#[should_panic(expected = "power of two")]
fn test_tx_ring_zero_capacity_panics() {
let _ = TxRing::new(0);
}
#[test]
fn test_xsk_socket_fallback_mode() {
let mut sock = XskSocket::fallback();
assert_eq!(sock.mode(), XskBindMode::FallbackRecvmmsg);
assert_eq!(sock.fd(), -1);
assert_eq!(sock.rx_ring_mut().capacity(), UMEM_RING_SIZE);
assert_eq!(sock.tx_ring_mut().capacity(), UMEM_RING_SIZE);
}
#[test]
fn test_bind_opportunistic_on_loopback() {
let umem = UmemPool::new(16, UMEM_CHUNK_SIZE).expect("allocate UMEM pool");
let res = XskSocket::bind_opportunistic("lo", 0, &umem);
assert!(res.is_ok(), "bind_opportunistic must not fail with Err");
let mut sock = res.unwrap();
match sock.mode() {
XskBindMode::FallbackRecvmmsg => {
assert_eq!(sock.fd(), -1);
}
XskBindMode::ZeroCopy | XskBindMode::Copy => {
assert!(sock.fd() >= 0);
}
}
assert!(sock.rx_ring_mut().is_empty());
assert!(sock.tx_ring_mut().is_empty());
}
#[test]
fn test_bind_opportunistic_on_invalid_interface_falls_back() {
let umem = UmemPool::new(16, UMEM_CHUNK_SIZE).expect("allocate UMEM pool");
let res = XskSocket::bind_opportunistic("nonexistent_dev_42", 0, &umem);
assert!(
res.is_ok(),
"bind_opportunistic must never panic or error out"
);
let sock = res.unwrap();
assert_eq!(sock.mode(), XskBindMode::FallbackRecvmmsg);
assert_eq!(sock.fd(), -1);
}
#[test]
fn test_xsk_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<XskBindMode>();
assert_send_sync::<XskDesc>();
assert_send_sync::<RxRing>();
assert_send_sync::<TxRing>();
assert_send_sync::<XskSocket>();
}
#[test]
fn test_bind_multi_queue() {
let umem = UmemPool::new(16, UMEM_CHUNK_SIZE).expect("allocate UMEM pool");
let queue_ids = vec![0, 1, 2, 3];
let res = XskSocket::bind_multi_queue("lo", &queue_ids, &umem);
assert!(res.is_ok());
let sockets = res.unwrap();
assert_eq!(sockets.len(), 4);
for (i, (qid, sock)) in sockets.iter().enumerate() {
assert_eq!(*qid, i as u32);
assert!(sock.rx_ring().is_empty());
}
}