use super::{AssociatedEndpoint, CompletionPort, Submitted};
use crate::{Operation, OperationState, UnassociatedEndpoint};
use std::fs::OpenOptions;
use std::os::windows::fs::OpenOptionsExt;
use std::os::windows::io::OwnedHandle;
use std::path::PathBuf;
const FILE_FLAG_OVERLAPPED: u32 = 0x4000_0000;
fn associate_temp_file<'port>(
port: &'port CompletionPort,
tag: &str,
) -> (AssociatedEndpoint<'port>, PathBuf) {
let path = std::env::temp_dir().join(format!(
"windows-overlapped-io-sys-{tag}-{}.tmp",
std::process::id()
));
let file = OpenOptions::new()
.create(true)
.read(true)
.write(true)
.custom_flags(FILE_FLAG_OVERLAPPED)
.open(&path)
.expect("create overlapped temp file");
let owned = OwnedHandle::from(file);
let endpoint = unsafe { UnassociatedEndpoint::assume_overlapped(owned) };
let associated = port.associate(endpoint, 0).expect("associate");
(associated, path)
}
#[test]
fn posts_and_dequeues_a_user_packet() {
let port = CompletionPort::new(0).expect("create port");
port.post(0xABCD, 42).expect("post packet");
let completion = port.get(1_000).expect("get packet").expect("a packet");
assert_eq!(completion.key(), 0xABCD);
assert_eq!(completion.bytes_transferred(), 42);
assert!(completion.overlapped_ptr().is_null());
assert!(completion.error().is_none());
}
#[test]
fn get_times_out_when_empty() {
let port = CompletionPort::new(0).expect("create port");
assert!(port.get(0).expect("get").is_none());
}
#[test]
fn associates_an_overlapped_handle() {
let port = CompletionPort::new(0).expect("create port");
let path = std::env::temp_dir().join(format!(
"windows-overlapped-io-sys-iocp-{}.tmp",
std::process::id()
));
let file = OpenOptions::new()
.create(true)
.read(true)
.write(true)
.custom_flags(FILE_FLAG_OVERLAPPED)
.open(&path)
.expect("create overlapped temp file");
let owned = OwnedHandle::from(file);
let endpoint = unsafe { UnassociatedEndpoint::assume_overlapped(owned) };
let associated = port.associate(endpoint, 0x55).expect("associate");
assert_eq!(associated.key(), 0x55);
drop(associated);
let _ = std::fs::remove_file(&path);
}
#[test]
fn submit_pending_then_claim_recovers_the_operation() {
let port = CompletionPort::new(0).expect("create port");
let (endpoint, path) = associate_temp_file(&port, "submit-pending");
let operation = Operation::new(vec![1_u8, 2, 3]);
let submitted = unsafe {
endpoint.submit(operation, |_handle, overlapped| {
port.post_raw(7, 3, overlapped)?;
Ok(())
})
};
assert!(matches!(submitted, Submitted::Pending(_)));
let Submitted::Pending(id) = submitted else {
unreachable!("just asserted pending");
};
assert_eq!(port.outstanding(), 1);
let completion = port.get(1_000).expect("get").expect("a packet");
assert_eq!(completion.overlapped_ptr(), id.as_ptr());
let operation = unsafe { completion.claim::<Vec<u8>>() };
assert_eq!(operation.state(), OperationState::Completed);
assert_eq!(operation.payload(), &vec![1_u8, 2, 3]);
assert_eq!(port.outstanding(), 0);
drop(endpoint);
let _ = std::fs::remove_file(&path);
}
#[test]
fn submit_immediate_failure_returns_the_operation() {
let port = CompletionPort::new(0).expect("create port");
let (endpoint, path) = associate_temp_file(&port, "submit-fail");
let operation = Operation::new(vec![9_u8]);
let submitted = unsafe {
endpoint.submit(operation, |_handle, _overlapped| {
Err(std::io::Error::from_raw_os_error(5))
})
};
match submitted {
Submitted::Failed { operation, error } => {
assert_eq!(operation.payload(), &vec![9_u8]);
assert_eq!(operation.state(), OperationState::Idle);
assert_eq!(error.raw_os_error(), Some(5));
}
Submitted::Pending(_) => panic!("expected immediate failure"),
}
assert_eq!(port.outstanding(), 0);
drop(endpoint);
let _ = std::fs::remove_file(&path);
}
#[test]
fn unclaimed_completion_reclaims_on_drop() {
let port = CompletionPort::new(0).expect("create port");
let (endpoint, path) = associate_temp_file(&port, "unclaimed");
let operation = Operation::new(vec![7_u8]);
let submitted = unsafe {
endpoint.submit(operation, |_handle, overlapped| {
port.post_raw(0, 1, overlapped)?;
Ok(())
})
};
assert!(matches!(submitted, Submitted::Pending(_)));
assert_eq!(port.outstanding(), 1);
let completion = port.get(1_000).expect("get").expect("a packet");
drop(completion);
assert_eq!(port.outstanding(), 0);
drop(endpoint);
let _ = std::fs::remove_file(&path);
}