#![allow(missing_docs)]
#![allow(clippy::unwrap_used)]
use std::fs::File;
use std::os::fd::{AsRawFd, FromRawFd};
use std::sync::{Arc, Barrier};
use std::thread;
use std::time::Duration;
use tempfile::NamedTempFile;
use wireshift_core::backend::{Backend, BackendSubmission, CancellationHandle};
use wireshift_core::config::RingConfig;
use wireshift_core::op::OpDescriptor;
use wireshift_uring::UringBackend;
#[test]
fn test_edge_queue_depth_exhaustion() {
let (tx, rx) = std::sync::mpsc::channel();
let config = RingConfig {
queue_depth: 8, ..RingConfig::default()
};
let backend = UringBackend::new(&config, tx).expect("backend creation");
let mut submitted_count = 0;
for i in 0..32 {
let submission = BackendSubmission {
timeout: None,
id: 1000 + i,
descriptor: OpDescriptor::Nop,
};
if backend.submit(submission).is_ok() {
submitted_count += 1;
}
}
assert!(submitted_count > 0, "At least some submissions should be accepted");
thread::sleep(Duration::from_millis(50));
while rx.try_recv().is_ok() {}
let _ = backend.shutdown();
}
#[test]
fn test_edge_submit_and_wait_timeout_boundary() {
let (tx, _rx) = std::sync::mpsc::channel();
let config = RingConfig {
queue_depth: 16,
..RingConfig::default()
};
let backend = UringBackend::new(&config, tx).expect("backend creation");
let mut fds = [0 as libc::c_int; 2];
let res = unsafe { libc::pipe(fds.as_mut_ptr()) };
assert_eq!(res, 0, "failed to create pipe");
let read_file = unsafe { File::from_raw_fd(fds[0]) };
let _write_file = unsafe { File::from_raw_fd(fds[1]) };
let buffer = wireshift_core::buffer::BufferPool::new(1024, 1)
.unwrap()
.acquire()
.unwrap()
.into_submitted();
let submission = BackendSubmission {
timeout: None,
id: 2000,
descriptor: OpDescriptor::Read {
file: read_file,
offset: 0,
buffer,
len: None,
},
};
backend.submit(submission).expect("submit stuck operation");
thread::sleep(Duration::from_millis(20));
let start = std::time::Instant::now();
let shutdown_res = backend.shutdown();
let elapsed = start.elapsed();
assert!(shutdown_res.is_ok(), "shutdown should succeed");
assert!(elapsed < Duration::from_secs(10), "shutdown took too long: {:?}", elapsed);
}
#[test]
fn test_edge_raw_fd_registrations() {
let (tx, _rx) = std::sync::mpsc::channel();
let config = RingConfig {
queue_depth: 32,
..RingConfig::default()
};
let backend = UringBackend::new(&config, tx).expect("backend creation");
let _ = backend.unregister_files();
let res_empty = backend.register_files(&[]);
assert!(res_empty.is_err(), "register empty files should fail in kernel");
let file1 = File::open("/dev/null").expect("open file1");
let file2 = File::open("/dev/null").expect("open file2");
let fds = [file1.as_raw_fd(), file2.as_raw_fd()];
let res_reg = backend.register_files(&fds);
assert!(res_reg.is_ok(), "registering valid fds should succeed");
let _ = backend.register_files(&fds);
let invalid_fds = [-1, 99999];
let res_invalid = backend.register_files(&invalid_fds);
assert!(res_invalid.is_err(), "registering invalid fds must fail");
let _ = backend.unregister_files();
let _ = backend.shutdown();
}
#[test]
fn test_edge_concurrent_submissions() {
let (tx, rx) = std::sync::mpsc::channel();
let config = RingConfig {
queue_depth: 128,
..RingConfig::default()
};
let backend = Arc::new(UringBackend::new(&config, tx).expect("backend creation"));
let num_threads = 8;
let ops_per_thread = 100;
let barrier = Arc::new(Barrier::new(num_threads));
let mut handles = Vec::new();
for t_idx in 0..num_threads {
let backend_clone = Arc::clone(&backend);
let barrier_clone = Arc::clone(&barrier);
handles.push(thread::spawn(move || {
barrier_clone.wait();
for i in 0..ops_per_thread {
let id = (t_idx as u64 * 1000) + i as u64;
let submission = BackendSubmission {
timeout: None,
id,
descriptor: OpDescriptor::Nop,
};
let _ = backend_clone.submit(submission);
if i % 10 == 0 {
let cancel_handle = CancellationHandle { target: id };
let _ = backend_clone.cancel(cancel_handle);
}
}
}));
}
for h in handles {
h.join().expect("thread join succeeded");
}
thread::sleep(Duration::from_millis(50));
while rx.try_recv().is_ok() {}
let _ = backend.shutdown();
}