Files
tsunagi/crates/tsunagi-core/tests/mainline_socket.rs
T
Aleksandr BogomiakovandClaude 9966b50407 Rename the CLI package to tsunagi and its executable to tsng
The command line package is now `tsunagi` and builds the `tsng` executable,
which the packages, the unit, the installers and the workflows install and
run. The core library package becomes `tsunagi-core`, keeping the library name
`tsunagi`, so no code changes `use tsunagi::...`. Directories follow
(crates/tsunagi-core, crates/tsunagi). Every command shown to a person is now
`tsng ...`.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-03 02:14:43 +01:00

69 lines
2.7 KiB
Rust

//! The real Mainline UDP receive loop must be quiet while idle on Windows too.
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use std::net::Ipv4Addr;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
use tracing_subscriber::layer::SubscriberExt;
struct SocketWarnings(Arc<AtomicUsize>);
impl<S: tracing::Subscriber> tracing_subscriber::Layer<S> for SocketWarnings {
fn on_event(&self, event: &tracing::Event<'_>, _: tracing_subscriber::layer::Context<'_, S>) {
if event.metadata().target() == "mainline::rpc::socket"
&& *event.metadata().level() <= tracing::Level::WARN
{
self.0.fetch_add(1, Ordering::Relaxed);
}
}
}
#[tokio::test]
async fn an_idle_dht_socket_stays_quiet_and_still_answers_packets() {
// Mainline owns an OS thread, so a thread-local subscriber cannot see its
// events. This integration-test binary has just this test and installs
// its own subscriber; it cannot affect the application or other binaries.
let warnings = Arc::new(AtomicUsize::new(0));
tracing::subscriber::set_global_default(
tracing_subscriber::registry().with(SocketWarnings(Arc::clone(&warnings))),
)
.unwrap();
tokio::time::timeout(Duration::from_secs(5), async {
let dht = mainline::Dht::builder()
.no_bootstrap()
.server_mode()
.bind_address(Ipv4Addr::LOCALHOST)
.port(0)
.build()
.unwrap()
.as_async();
let address = dht.info().await.local_addr();
// Each info response comes from the actor after another receive-loop
// iteration. With no peers or packets, these exercise real socket
// read timeouts, without an arbitrary sleep or public bootstrap.
for _ in 0..8 {
assert_eq!(dht.info().await.local_addr(), address);
}
assert_eq!(warnings.load(Ordering::Relaxed), 0, "idle receive warnings");
let client = tokio::net::UdpSocket::bind((Ipv4Addr::LOCALHOST, 0))
.await
.unwrap();
let ping = b"d1:ad2:id20:abcdefghijklmnopqrste1:q4:ping1:t4:ping1:y1:qe";
client.send_to(ping, address).await.unwrap();
let mut response = [0u8; 2048];
let (len, from) = client.recv_from(&mut response).await.unwrap();
assert_eq!(from, std::net::SocketAddr::V4(address));
let response = &response[..len];
assert!(response.windows(9).any(|field| field == b"1:t4:ping"));
assert!(response.windows(6).any(|field| field == b"1:y1:r"));
assert_eq!(warnings.load(Ordering::Relaxed), 0);
})
.await
.expect("the DHT receive loop keeps making progress");
}