use std::net::TcpListener;
use std::sync::atomic::{AtomicU16, Ordering};
static NEXT_PORT: AtomicU16 = AtomicU16::new(40000);
pub fn get_free_port() -> u16 {
match TcpListener::bind("127.0.0.1:0") {
Ok(listener) => {
let port = listener.local_addr().unwrap().port();
drop(listener); port
}
Err(_) => {
NEXT_PORT.fetch_add(1, Ordering::SeqCst)
}
}
}
pub fn get_free_port_for(protocol: &str) -> u16 {
let port = get_free_port();
tracing::debug!("Allocated port {} for {} testing", port, protocol);
port
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
#[test]
fn test_unique_ports() {
let mut ports = HashSet::new();
for _ in 0..10 {
let port = get_free_port();
assert!(ports.insert(port), "Port {} was already allocated", port);
assert!(port > 1024, "Port {} is in privileged range", port);
}
}
#[test]
fn test_ports_are_actually_free() {
let port = get_free_port();
let listener = TcpListener::bind(format!("127.0.0.1:{}", port));
assert!(
listener.is_ok(),
"Could not bind to supposedly free port {}",
port
);
}
}