mod common;
use atap::{
Runtime, RuntimeError,
udp::{Udp, UdpSocket},
};
use common::{until_started, within};
use std::{
net::Ipv4Addr,
time::{Duration, Instant},
};
const PATIENCE: Duration = Duration::from_secs(10);
fn socket() -> UdpSocket {
let _ = Runtime::init();
Runtime::block(Udp::bind("127.0.0.1:0")).expect("a loopback socket must bind")
}
#[test]
fn a_datagram_arrives_with_its_sender() {
let a = socket();
let b = socket();
let sent = Runtime::block(a.send_to(b.local_addr(), b"hello".as_slice())).unwrap();
assert_eq!(sent, 5);
let (data, from) = within(b.recv_from(), PATIENCE).unwrap();
assert_eq!(data, b"hello");
assert_eq!(from, a.local_addr());
}
#[test]
fn datagrams_keep_their_boundaries() {
let a = socket();
let b = socket();
Runtime::block(a.send_to(b.local_addr(), b"one".as_slice())).unwrap();
Runtime::block(a.send_to(b.local_addr(), b"two".as_slice())).unwrap();
assert_eq!(within(b.recv_from(), PATIENCE).unwrap().0, b"one");
assert_eq!(within(b.recv_from(), PATIENCE).unwrap().0, b"two");
}
#[test]
fn an_empty_datagram_is_a_real_one() {
let a = socket();
let b = socket();
assert_eq!(
Runtime::block(a.send_to(b.local_addr(), b"".as_slice())).unwrap(),
0
);
let (data, from) = within(b.recv_from(), PATIENCE).unwrap();
assert!(data.is_empty());
assert_eq!(from, a.local_addr());
}
#[test]
fn a_large_datagram_arrives_whole() {
let a = socket();
let b = socket();
let data: Vec<u8> = (0..8000).map(|at| (at % 251) as u8).collect();
Runtime::block(a.send_to(b.local_addr(), data.clone())).unwrap();
assert_eq!(within(b.recv_from(), PATIENCE).unwrap().0, data);
}
#[test]
fn a_spawned_receive_waits_for_a_datagram() {
let a = socket();
let b = socket();
let reading = Runtime::task(b.recv_from()).spawn();
until_started(&reading, PATIENCE);
assert!(
reading.is_running(),
"a receive waiting on the network reads as running, got {:?}",
reading.state(),
);
Runtime::block(a.send_to(b.local_addr(), b"late".as_slice())).unwrap();
let (data, from) = reading
.take_with_timeout(PATIENCE)
.expect("the receive must settle once a datagram is there")
.expect("the receive must succeed");
assert_eq!(data, b"late");
assert_eq!(from, a.local_addr());
}
#[test]
fn a_blocking_receive_times_out() {
let b = socket();
let started = Instant::now();
let got = within(b.recv_from(), Duration::from_millis(100));
let took = started.elapsed();
assert_eq!(got, Err(RuntimeError::TimedOut));
assert!(
took >= Duration::from_millis(100),
"gave up early, after {took:?}"
);
assert!(
took < Duration::from_secs(2),
"gave up late, after {took:?}"
);
}
#[test]
fn a_spawned_receive_times_out() {
let b = socket();
let handle = Runtime::task(b.recv_from())
.timeout(Duration::from_millis(100))
.spawn();
assert_eq!(
handle.take_with_timeout(PATIENCE),
Err(RuntimeError::TimedOut),
);
}
#[test]
fn a_parked_receive_can_be_cancelled() {
let b = socket();
let reading = Runtime::task(b.recv_from()).spawn();
until_started(&reading, PATIENCE);
reading.clone().cancel();
assert_eq!(
reading.join_with_timeout(PATIENCE),
Err(RuntimeError::Cancelled),
);
}
#[test]
fn a_name_is_looked_up_in_the_sockets_family() {
let a = socket();
let b = socket();
let port = b.local_addr().port();
Runtime::block(a.send_to(format!("localhost:{port}"), b"named".as_slice()))
.expect("localhost has an IPv4 address to send to");
assert_eq!(within(b.recv_from(), PATIENCE).unwrap().0, b"named");
}
#[test]
fn a_nonsense_address_is_a_bad_address() {
let a = socket();
assert_eq!(
Runtime::block(a.send_to("not an address", b"x".as_slice())),
Err(RuntimeError::BadAddress),
);
}
#[test]
fn an_address_in_the_other_family_is_a_bad_address() {
let a = socket();
assert_eq!(
Runtime::block(a.send_to("[::1]:9", b"x".as_slice())),
Err(RuntimeError::BadAddress),
);
}
#[test]
fn a_receive_in_flight_outlives_a_close() {
let a = socket();
let b = socket();
let to = b.local_addr();
let reading = Runtime::task(b.recv_from()).spawn();
until_started(&reading, PATIENCE);
b.close();
Runtime::block(a.send_to(to, b"still".as_slice())).unwrap();
assert_eq!(
reading.take_with_timeout(PATIENCE).unwrap().unwrap().0,
b"still"
);
}
#[test]
fn socket_settings_take() {
let _ = Runtime::init();
let socket = Runtime::block(Udp::bind("0.0.0.0:0").broadcast(true)).unwrap();
socket.set_broadcast(false).unwrap();
socket.set_ttl(7).unwrap();
assert_eq!(socket.ttl(), Ok(7));
let group = Ipv4Addr::new(239, 255, 42, 99);
socket
.join_multicast_v4(group, Ipv4Addr::UNSPECIFIED)
.unwrap();
socket
.leave_multicast_v4(group, Ipv4Addr::UNSPECIFIED)
.unwrap();
assert_eq!(socket.set_ttl(u32::MAX), Err(RuntimeError::BadArgument));
let v6 = Runtime::block(Udp::bind("[::1]:0").v6_only(true)).unwrap();
v6.set_ttl(3).unwrap();
assert_eq!(v6.ttl(), Ok(3));
}
#[test]
fn reused_udp_ports_are_shared() {
let _ = Runtime::init();
let first = Runtime::block(Udp::bind("127.0.0.1:0").reuse_port(true)).unwrap();
let second = Runtime::block(Udp::bind(first.local_addr()).reuse_port(true));
assert!(second.is_ok(), "the port was not shared: {second:?}");
}
#[test]
fn a_connected_socket_needs_no_address() {
let a = socket();
let b = socket();
let stranger = socket();
assert_eq!(
a.peer_addr(),
Err(RuntimeError::CheckError(Some(libc::ENOTCONN)))
);
assert_eq!(
Runtime::block(a.send(b"nowhere".as_slice())),
Err(RuntimeError::CheckError(Some(libc::EDESTADDRREQ)))
);
Runtime::block(a.connect(b.local_addr())).unwrap();
Runtime::block(b.connect(a.local_addr())).unwrap();
assert_eq!(a.peer_addr(), Ok(b.local_addr()));
Runtime::block(stranger.send_to(b.local_addr(), b"not for you".as_slice())).unwrap();
Runtime::block(a.send(b"for b".as_slice())).unwrap();
assert_eq!(within(b.recv(), PATIENCE), Ok(b"for b".to_vec()));
Runtime::block(b.send(b"back".as_slice())).unwrap();
assert_eq!(within(a.recv(), PATIENCE), Ok(b"back".to_vec()));
}
#[test]
fn a_peek_leaves_the_datagram() {
let a = socket();
let b = socket();
Runtime::block(a.send_to(b.local_addr(), b"twice".as_slice())).unwrap();
let (peeked, from) = within(b.recv_from().peek(), PATIENCE).unwrap();
assert_eq!(peeked, b"twice");
assert_eq!(from, a.local_addr());
assert_eq!(within(b.recv_from(), PATIENCE).unwrap().0, b"twice");
Runtime::block(b.connect(a.local_addr())).unwrap();
Runtime::block(a.send_to(b.local_addr(), b"again".as_slice())).unwrap();
assert_eq!(within(b.recv().peek(), PATIENCE), Ok(b"again".to_vec()));
assert_eq!(within(b.recv(), PATIENCE), Ok(b"again".to_vec()));
}