From 454be72769a6434c2193ceb7fea5d18030703dbe Mon Sep 17 00:00:00 2001 From: Brandon Pike Date: Sun, 3 Nov 2024 09:18:38 -0800 Subject: [PATCH 1/2] Treat partitions as unreachable hosts Instead of returning `ErrorKind::ConnectionRefused` for all deliver failures, this commit associates partitions with the same error as a link not being found -> host unreachable. The `ErrorKind` in this commit is Other, pending `ErrorKind::HostUnreachable` stabilization. --- examples/README.md | 10 +++++ examples/axum/Cargo.toml | 2 +- examples/grpc/Cargo.toml | 2 +- src/net/tcp/stream.rs | 41 ++++++++----------- src/sim.rs | 72 +++++++++++---------------------- src/top.rs | 86 +++++++++++++++++++++++++--------------- tests/tcp.rs | 51 +++++++++++------------- tests/udp.rs | 48 ++++++++++------------ 8 files changed, 150 insertions(+), 162 deletions(-) create mode 100644 examples/README.md diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..aeb0536 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,10 @@ +# Turmoil examples + +You can run these `turmoil` examples with this command: `cargo run -p `. For example: + +``` +$ cargo run -p axum-example + +# To turn on logs +$ RUST_LOG=debug cargo run -p axum-example +``` diff --git a/examples/axum/Cargo.toml b/examples/axum/Cargo.toml index a02a379..191c901 100644 --- a/examples/axum/Cargo.toml +++ b/examples/axum/Cargo.toml @@ -13,5 +13,5 @@ turmoil = { path = "../.." } tracing = "0.1" tracing-subscriber = "0.3" tokio = "1" -tower = "0.4" +tower = { version = "0.4.13", features = ["util"] } pin-project-lite = "0.2" diff --git a/examples/grpc/Cargo.toml b/examples/grpc/Cargo.toml index e845be7..e0e84dd 100644 --- a/examples/grpc/Cargo.toml +++ b/examples/grpc/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "grpc" +name = "grpc-example" version = "0.1.0" edition = "2021" publish = false diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index a4d38a0..23cd3a5 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -1,30 +1,23 @@ -use std::{ - fmt::Debug, - io::{self, Error, Result}, - net::SocketAddr, - pin::Pin, - sync::Arc, - task::{ready, Context, Poll}, -}; +use std::fmt::Debug; +use std::io::{self, Error, Result}; +use std::net::SocketAddr; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{ready, Context, Poll}; use bytes::{Buf, Bytes}; -use tokio::{ - io::{AsyncRead, AsyncWrite, ReadBuf}, - runtime::Handle, - sync::{mpsc, oneshot}, - time::sleep, -}; - -use crate::{ - envelope::{Envelope, Protocol, Segment, Syn}, - host::is_same, - host::SequencedSegment, - net::SocketPair, - world::World, - ToSocketAddrs, TRACING_TARGET, -}; +use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; +use tokio::runtime::Handle; +use tokio::sync::{mpsc, oneshot}; +use tokio::time::sleep; use super::split_owned::{OwnedReadHalf, OwnedWriteHalf}; +use crate::envelope::{Envelope, Protocol, Segment, Syn}; +use crate::host::is_same; +use crate::host::SequencedSegment; +use crate::net::SocketPair; +use crate::world::World; +use crate::{ToSocketAddrs, TRACING_TARGET}; /// A simulated TCP stream between a local and a remote socket. /// @@ -430,4 +423,4 @@ impl Drop for WriteHalf { world.current_host_mut().tcp.close_stream_half(*self.pair); }) } -} \ No newline at end of file +} diff --git a/src/sim.rs b/src/sim.rs index f642857..868550e 100644 --- a/src/sim.rs +++ b/src/sim.rs @@ -1,4 +1,3 @@ -use rand::seq::SliceRandom; use std::cell::RefCell; use std::future::Future; use std::net::IpAddr; @@ -7,11 +6,12 @@ use std::sync::Arc; use std::time::UNIX_EPOCH; use indexmap::IndexMap; +use rand::seq::SliceRandom; use tokio::time::Duration; use tracing::Level; -use crate::{Config, for_pairs, LinksIter, Result, Rt, ToIpAddr, ToIpAddrs, TRACING_TARGET, World}; use crate::host::HostTimer; +use crate::{for_pairs, Config, LinksIter, Result, Rt, ToIpAddr, ToIpAddrs, World, TRACING_TARGET}; /// A handle for interacting with the simulation. pub struct Sim<'a> { @@ -430,17 +430,16 @@ impl<'a> Sim<'a> { #[cfg(test)] mod test { use rand::Rng; + use std::{future, io}; use std::{ net::{IpAddr, Ipv4Addr}, rc::Rc, sync::{ - Mutex, - Arc, atomic::{AtomicU64, Ordering}, + Arc, Mutex, }, time::Duration, }; - use std::future; use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, @@ -450,9 +449,9 @@ mod test { use crate::net::UdpSocket; use crate::{ - Builder, elapsed, - hold, - net::{TcpListener, TcpStream}, Result, Sim, sim_elapsed, World, + elapsed, hold, + net::{TcpListener, TcpStream}, + sim_elapsed, Builder, Result, Sim, World, }; #[test] @@ -674,7 +673,9 @@ mod test { sim.client("client", async move { // Peers are partitioned. TCP setup should fail. - let _ = TcpStream::connect("server:1234").await.unwrap_err(); + let err = TcpStream::connect("server:1234").await.unwrap_err(); + assert_eq!(err.kind(), io::ErrorKind::Other); + assert_eq!(err.to_string(), "host unreachable"); Ok(()) }); @@ -686,7 +687,6 @@ mod test { Ok(()) } - struct Expectation { expect_a_receive: bool, expect_b_receive: bool, @@ -723,10 +723,9 @@ mod test { async move { let udp_socket = UdpSocket::bind((IpAddr::V4(Ipv4Addr::UNSPECIFIED), 1234)).await?; - udp_socket - .send_to(&[42], format!("{}:1234", host_b)) - .await - .expect("sending packet should appear to work, even if partitioned"); + // If hosts are partitioned, this will return an unreachable + // error. + let _ = udp_socket.send_to(&[42], format!("{}:1234", host_b)).await; *a_did_receive.lock().unwrap() = Some(matches!( tokio::time::timeout( @@ -748,10 +747,9 @@ mod test { async move { let udp_socket = UdpSocket::bind((IpAddr::V4(Ipv4Addr::UNSPECIFIED), 1234)).await?; - udp_socket - .send_to(&[42], format!("{}:1234", host_a)) - .await - .expect("sending packet should work"); + // If hosts are partitioned, this will return an unreachable + // error. + let _ = udp_socket.send_to(&[42], format!("{}:1234", host_a)).await; *b_did_receive.lock().unwrap() = Some(matches!( tokio::time::timeout( @@ -848,41 +846,19 @@ mod test { Ok(()) } - run_with_actions(&[ - Action::PartitionOnewayAB, - ])?; - run_with_actions(&[ - Action::PartitionOnewayBA, - ])?; + run_with_actions(&[Action::PartitionOnewayAB])?; + run_with_actions(&[Action::PartitionOnewayBA])?; + run_with_actions(&[Action::Partition, Action::RepairOnewayAB])?; + run_with_actions(&[Action::Partition, Action::RepairOnewayBA])?; + run_with_actions(&[Action::PartitionOnewayAB, Action::Repair])?; + run_with_actions(&[Action::PartitionOnewayBA, Action::Repair])?; + run_with_actions(&[Action::PartitionOnewayBA, Action::RepairOnewayAB])?; + run_with_actions(&[Action::PartitionOnewayAB, Action::PartitionOnewayBA])?; run_with_actions(&[ Action::Partition, Action::RepairOnewayAB, - ])?; - run_with_actions(&[ - Action::Partition, Action::RepairOnewayBA, ])?; - run_with_actions(&[ - Action::PartitionOnewayAB, - Action::Repair, - ])?; - run_with_actions(&[ - Action::PartitionOnewayBA, - Action::Repair, - ])?; - run_with_actions(&[ - Action::PartitionOnewayBA, - Action::RepairOnewayAB, - ])?; - run_with_actions(&[ - Action::PartitionOnewayAB, - Action::PartitionOnewayBA, - ])?; - run_with_actions(&[ - Action::Partition, - Action::RepairOnewayAB, - Action::RepairOnewayBA - ])?; Ok(()) } diff --git a/src/top.rs b/src/top.rs index fd7a955..f810112 100644 --- a/src/top.rs +++ b/src/top.rs @@ -1,17 +1,18 @@ -use crate::envelope::{Envelope, Protocol}; -use crate::host::Host; -use crate::rt::Rt; -use crate::{config, TRACING_TARGET}; - -use indexmap::IndexMap; -use rand::{Rng, RngCore}; -use rand_distr::{Distribution, Exp}; use std::collections::VecDeque; use std::io::{Error, ErrorKind, Result}; use std::net::{IpAddr, SocketAddr}; use std::time::Duration; + +use indexmap::IndexMap; +use rand::{Rng, RngCore}; +use rand_distr::{Distribution, Exp}; use tokio::time::Instant; +use crate::envelope::{Envelope, Protocol}; +use crate::host::Host; +use crate::rt::Rt; +use crate::{config, TRACING_TARGET}; + /// Describes the network topology. pub(crate) struct Topology { config: config::Link, @@ -184,7 +185,10 @@ impl Topology { /// Register a link between two hosts pub(crate) fn register(&mut self, a: IpAddr, b: IpAddr) { let pair = Pair::new(a, b); - assert!(self.links.insert(pair, Link::new(self.rt.now())).is_none()); + assert!(self + .links + .insert(pair, Link::new(self.rt.now(), self.config.clone())) + .is_none()); } pub(crate) fn set_max_message_latency(&mut self, value: Duration) { @@ -227,14 +231,23 @@ impl Topology { dst: SocketAddr, message: Protocol, ) -> Result<()> { - if let Some(link) = self.links.get_mut(&Pair::new(src.ip(), dst.ip())) { + if let Some(link) = self + .links + .get_mut(&Pair::new(src.ip(), dst.ip())) + // Treat partitions as unlinked hosts + .and_then(|l| match l.get_state_for_message(src.ip(), dst.ip()) { + State::Healthy | State::Hold => Some(l), + State::ExplicitPartition | State::RandPartition => None, + }) + { link.enqueue_message(&self.config, rand, src, dst, message); Ok(()) } else { - Err(Error::new( - ErrorKind::ConnectionRefused, - "Connection refused", - )) + // TODO: `ErrorKind::Other` is incorrect here, but + // `ErrorKind::HostUnreachable` has not been stabilized. + // + // See: https://github.com/rust-lang/rust/issues/86442 + Err(Error::new(ErrorKind::Other, "host unreachable")) } } @@ -307,11 +320,11 @@ enum DeliveryStatus { } impl Link { - fn new(now: Instant) -> Link { + fn new(now: Instant, config: config::Link) -> Link { Link { state_a_b: State::Healthy, state_b_a: State::Healthy, - config: config::Link::default(), + config, sent: VecDeque::new(), deliverable: IndexMap::new(), now, @@ -442,13 +455,14 @@ impl Link { } // Randomly break or repair this link. + // + // FIXME: pull this operation up to the topology level. A link shouldn't be + // responsible for determining its own random failure. fn rand_partition_or_repair(&mut self, global_config: &config::Link, rand: &mut dyn RngCore) { - let do_rand = self.rand_partition(global_config.message_loss(), rand); match (self.state_a_b, self.state_b_a) { (State::Healthy, _) | (_, State::Healthy) => { - if do_rand { - self.state_a_b = State::RandPartition; - self.state_b_a = State::RandPartition; + if self.rand_fail(global_config.message_loss(), rand) { + self.rand_partition(); } } (State::RandPartition, _) | (_, State::RandPartition) => { @@ -460,6 +474,20 @@ impl Link { } } + /// With some chance `config.fail_rate`, randomly partition this link. + fn rand_fail(&self, global: &config::MessageLoss, rand: &mut dyn RngCore) -> bool { + let config = self.config.message_loss.as_ref().unwrap_or(global); + let fail_rate = config.fail_rate; + fail_rate > 0.0 && rand.gen_bool(fail_rate) + } + + /// With some chance `config.repair_rate`, randomly repair this link. + fn rand_repair(&self, global: &config::MessageLoss, rand: &mut dyn RngCore) -> bool { + let config = self.config.message_loss.as_ref().unwrap_or(global); + let repair_rate = config.repair_rate; + repair_rate > 0.0 && rand.gen_bool(repair_rate) + } + fn hold(&mut self) { self.state_a_b = State::Hold; self.state_b_a = State::Hold; @@ -476,6 +504,11 @@ impl Link { } } + fn rand_partition(&mut self) { + self.state_a_b = State::RandPartition; + self.state_b_a = State::RandPartition; + } + fn explicit_partition(&mut self) { self.state_a_b = State::ExplicitPartition; self.state_b_a = State::ExplicitPartition; @@ -503,19 +536,6 @@ impl Link { self.state_b_a = State::Healthy; } - /// Should the link be randomly partitioned - fn rand_partition(&self, global: &config::MessageLoss, rand: &mut dyn RngCore) -> bool { - let config = self.config.message_loss.as_ref().unwrap_or(global); - let fail_rate = config.fail_rate; - fail_rate > 0.0 && rand.gen_bool(fail_rate) - } - - fn rand_repair(&self, global: &config::MessageLoss, rand: &mut dyn RngCore) -> bool { - let config = self.config.message_loss.as_ref().unwrap_or(global); - let repair_rate = config.repair_rate; - repair_rate > 0.0 && rand.gen_bool(repair_rate) - } - fn delay(&self, global: &config::Latency, rand: &mut dyn RngCore) -> Duration { let config = self.config.latency.as_ref().unwrap_or(global); diff --git a/tests/tcp.rs b/tests/tcp.rs index 54baa37..1238b58 100644 --- a/tests/tcp.rs +++ b/tests/tcp.rs @@ -1,22 +1,16 @@ -use std::{ - assert_eq, assert_ne, - io::{self, ErrorKind}, - net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, - rc::Rc, - time::Duration, -}; - use std::future; -use tokio::{ - io::{AsyncReadExt, AsyncWriteExt}, - sync::{oneshot, Notify}, - time::timeout, -}; -use turmoil::{ - lookup, - net::{TcpListener, TcpStream}, - Builder, IpVersion, Result, -}; +use std::io; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use std::rc::Rc; +use std::time::Duration; +use std::{assert_eq, assert_ne}; + +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::sync::{oneshot, Notify}; +use tokio::time::timeout; +use turmoil::lookup; +use turmoil::net::{TcpListener, TcpStream}; +use turmoil::{Builder, IpVersion, Result}; const PORT: u16 = 1738; @@ -50,10 +44,9 @@ fn network_partitions_during_connect() -> Result { sim.client("client", async { turmoil::partition("client", "server"); - assert_error_kind( - TcpStream::connect(("server", PORT)).await, - io::ErrorKind::ConnectionRefused, - ); + let err = TcpStream::connect(("server", PORT)).await.unwrap_err(); + assert_eq!(err.kind(), io::ErrorKind::Other); + assert_eq!(err.to_string(), "host unreachable"); turmoil::repair("client", "server"); turmoil::hold("client", "server"); @@ -226,7 +219,9 @@ fn network_partition_once_connected() -> Result { assert!(timeout(Duration::from_secs(1), s.read_u8()).await.is_err()); - s.write_u8(1).await?; + let err = s.write_u8(1).await.unwrap_err(); + assert_eq!(err.kind(), io::ErrorKind::Other); + assert_eq!(err.to_string(), "host unreachable"); Ok(()) }); @@ -236,9 +231,9 @@ fn network_partition_once_connected() -> Result { turmoil::partition("server", "client"); - s.write_u8(1).await?; - - assert!(timeout(Duration::from_secs(1), s.read_u8()).await.is_err()); + let err = s.write_u8(1).await.unwrap_err(); + assert_eq!(err.kind(), io::ErrorKind::Other); + assert_eq!(err.to_string(), "host unreachable"); Ok(()) }); @@ -1134,8 +1129,8 @@ fn socket_to_nonexistent_node() -> Result { ); let err = sock.unwrap_err(); - assert_eq!(err.kind(), ErrorKind::ConnectionRefused); - assert_eq!(err.to_string(), "Connection refused"); + assert_eq!(err.kind(), io::ErrorKind::Other); + assert_eq!(err.to_string(), "host unreachable"); Ok(()) }); diff --git a/tests/udp.rs b/tests/udp.rs index b4f5925..9a38311 100644 --- a/tests/udp.rs +++ b/tests/udp.rs @@ -1,17 +1,13 @@ -use std::{ - io::{self, ErrorKind}, - net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, - rc::Rc, - sync::{atomic::AtomicUsize, atomic::Ordering}, - time::Duration, -}; +use std::io::{self, ErrorKind}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use std::rc::Rc; +use std::sync::{atomic::AtomicUsize, atomic::Ordering}; +use std::time::Duration; use tokio::{sync::oneshot, time::timeout}; -use turmoil::{ - lookup, - net::{self, UdpSocket}, - Builder, IpVersion, Result, -}; +use turmoil::lookup; +use turmoil::net::{self, UdpSocket}; +use turmoil::{Builder, IpVersion, Result}; const PORT: u16 = 1738; @@ -31,10 +27,8 @@ async fn bind_to_v6(port: u16) -> std::result::Result Result<()> { - sock.send_to(b"ping", (lookup("server"), 1738)).await?; - - Ok(()) +async fn send_ping(sock: &net::UdpSocket) -> io::Result { + sock.send_to(b"ping", (lookup("server"), 1738)).await } fn try_send_ping(sock: &net::UdpSocket) -> Result<()> { @@ -240,11 +234,9 @@ fn network_partition() -> Result { turmoil::partition("client", "server"); let sock = bind().await?; - send_ping(&sock).await?; - - assert!(timeout(Duration::from_secs(1), recv_pong(&sock)) - .await - .is_err()); + let err = send_ping(&sock).await.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::Other); + assert_eq!(err.to_string(), "host unreachable"); Ok(()) }); @@ -569,8 +561,9 @@ fn loopback_localhost_public_v4() -> Result { let bind_addr = SocketAddr::new(bind_addr.ip(), 0); let socket = UdpSocket::bind(bind_addr).await?; - let res = socket.send_to(&expected, connect_addr).await; - assert_error_kind(res, io::ErrorKind::ConnectionRefused); + let err = socket.send_to(&expected, connect_addr).await.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::Other); + assert_eq!(err.to_string(), "host unreachable"); Ok(()) }); @@ -620,8 +613,9 @@ fn loopback_localhost_public_v6() -> Result { let bind_addr = SocketAddr::new(bind_addr.ip(), 0); let socket = UdpSocket::bind(bind_addr).await?; - let res = socket.send_to(&expected, connect_addr).await; - assert_error_kind(res, io::ErrorKind::ConnectionRefused); + let err = socket.send_to(&expected, connect_addr).await.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::Other); + assert_eq!(err.to_string(), "host unreachable"); Ok(()) }); @@ -729,8 +723,8 @@ fn socket_to_nonexistent_node() -> Result { ); let err = send.unwrap_err(); - assert_eq!(err.kind(), ErrorKind::ConnectionRefused); - assert_eq!(err.to_string(), "Connection refused"); + assert_eq!(err.kind(), ErrorKind::Other); + assert_eq!(err.to_string(), "host unreachable"); Ok(()) }); From a46830ffe148e7d1008f028c738d810e78f5d2a3 Mon Sep 17 00:00:00 2001 From: Brandon Pike Date: Sat, 30 Nov 2024 15:31:08 -0800 Subject: [PATCH 2/2] Update Other errors to HostUnreachable Rust 1.83 has dropped and with it comes new error kinds! --- Cargo.toml | 3 +++ src/dns.rs | 4 ++-- src/sim.rs | 2 +- src/top.rs | 10 +++------- tests/tcp.rs | 8 ++++---- tests/udp.rs | 8 ++++---- 6 files changed, 17 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d9b8790..8720e99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,9 @@ categories = ["asynchronous", "network-programming", "simulation"] [workspace] members = ["examples/*"] +[workspace.package] +rust-version = "1.83" + [dependencies] bytes = "1.4" futures = "0.3" diff --git a/src/dns.rs b/src/dns.rs index 352a1e3..ff73921 100644 --- a/src/dns.rs +++ b/src/dns.rs @@ -63,7 +63,7 @@ impl ToIpAddr for String { } } -impl<'a> ToIpAddr for &'a str { +impl ToIpAddr for &'_ str { fn to_ip_addr(&self, dns: &mut Dns) -> IpAddr { if let Ok(ipaddr) = self.parse() { return ipaddr; @@ -121,7 +121,7 @@ impl ToSocketAddrs for (String, u16) { } } -impl<'a> ToSocketAddrs for (&'a str, u16) { +impl ToSocketAddrs for (&'_ str, u16) { fn to_socket_addr(&self, dns: &Dns) -> SocketAddr { // When IP address is passed directly as a str. if let Ok(ip) = self.0.parse::() { diff --git a/src/sim.rs b/src/sim.rs index 868550e..3cf6b8a 100644 --- a/src/sim.rs +++ b/src/sim.rs @@ -674,7 +674,7 @@ mod test { sim.client("client", async move { // Peers are partitioned. TCP setup should fail. let err = TcpStream::connect("server:1234").await.unwrap_err(); - assert_eq!(err.kind(), io::ErrorKind::Other); + assert_eq!(err.kind(), io::ErrorKind::HostUnreachable); assert_eq!(err.to_string(), "host unreachable"); Ok(()) diff --git a/src/top.rs b/src/top.rs index f810112..98574a6 100644 --- a/src/top.rs +++ b/src/top.rs @@ -59,7 +59,7 @@ pub struct LinkIter<'a> { iter: std::collections::vec_deque::IterMut<'a, Sent>, } -impl<'a> LinkIter<'a> { +impl LinkIter<'_> { /// The [`IpAddr`] pair for the link. Always ordered to uniquely identify /// the link. pub fn pair(&self) -> (IpAddr, IpAddr) { @@ -84,7 +84,7 @@ pub struct SentRef<'a> { sent: &'a mut Sent, } -impl<'a> SentRef<'a> { +impl SentRef<'_> { /// The (src, dst) [`SocketAddr`] pair for the message. pub fn pair(&self) -> (SocketAddr, SocketAddr) { (self.src, self.dst) @@ -243,11 +243,7 @@ impl Topology { link.enqueue_message(&self.config, rand, src, dst, message); Ok(()) } else { - // TODO: `ErrorKind::Other` is incorrect here, but - // `ErrorKind::HostUnreachable` has not been stabilized. - // - // See: https://github.com/rust-lang/rust/issues/86442 - Err(Error::new(ErrorKind::Other, "host unreachable")) + Err(Error::new(ErrorKind::HostUnreachable, "host unreachable")) } } diff --git a/tests/tcp.rs b/tests/tcp.rs index 1238b58..e379aeb 100644 --- a/tests/tcp.rs +++ b/tests/tcp.rs @@ -45,7 +45,7 @@ fn network_partitions_during_connect() -> Result { turmoil::partition("client", "server"); let err = TcpStream::connect(("server", PORT)).await.unwrap_err(); - assert_eq!(err.kind(), io::ErrorKind::Other); + assert_eq!(err.kind(), io::ErrorKind::HostUnreachable); assert_eq!(err.to_string(), "host unreachable"); turmoil::repair("client", "server"); @@ -220,7 +220,7 @@ fn network_partition_once_connected() -> Result { assert!(timeout(Duration::from_secs(1), s.read_u8()).await.is_err()); let err = s.write_u8(1).await.unwrap_err(); - assert_eq!(err.kind(), io::ErrorKind::Other); + assert_eq!(err.kind(), io::ErrorKind::HostUnreachable); assert_eq!(err.to_string(), "host unreachable"); Ok(()) @@ -232,7 +232,7 @@ fn network_partition_once_connected() -> Result { turmoil::partition("server", "client"); let err = s.write_u8(1).await.unwrap_err(); - assert_eq!(err.kind(), io::ErrorKind::Other); + assert_eq!(err.kind(), io::ErrorKind::HostUnreachable); assert_eq!(err.to_string(), "host unreachable"); Ok(()) @@ -1129,7 +1129,7 @@ fn socket_to_nonexistent_node() -> Result { ); let err = sock.unwrap_err(); - assert_eq!(err.kind(), io::ErrorKind::Other); + assert_eq!(err.kind(), io::ErrorKind::HostUnreachable); assert_eq!(err.to_string(), "host unreachable"); Ok(()) diff --git a/tests/udp.rs b/tests/udp.rs index 9a38311..9f2083a 100644 --- a/tests/udp.rs +++ b/tests/udp.rs @@ -235,7 +235,7 @@ fn network_partition() -> Result { let sock = bind().await?; let err = send_ping(&sock).await.unwrap_err(); - assert_eq!(err.kind(), ErrorKind::Other); + assert_eq!(err.kind(), ErrorKind::HostUnreachable); assert_eq!(err.to_string(), "host unreachable"); Ok(()) @@ -562,7 +562,7 @@ fn loopback_localhost_public_v4() -> Result { let bind_addr = SocketAddr::new(bind_addr.ip(), 0); let socket = UdpSocket::bind(bind_addr).await?; let err = socket.send_to(&expected, connect_addr).await.unwrap_err(); - assert_eq!(err.kind(), ErrorKind::Other); + assert_eq!(err.kind(), ErrorKind::HostUnreachable); assert_eq!(err.to_string(), "host unreachable"); Ok(()) @@ -614,7 +614,7 @@ fn loopback_localhost_public_v6() -> Result { let bind_addr = SocketAddr::new(bind_addr.ip(), 0); let socket = UdpSocket::bind(bind_addr).await?; let err = socket.send_to(&expected, connect_addr).await.unwrap_err(); - assert_eq!(err.kind(), ErrorKind::Other); + assert_eq!(err.kind(), ErrorKind::HostUnreachable); assert_eq!(err.to_string(), "host unreachable"); Ok(()) @@ -723,7 +723,7 @@ fn socket_to_nonexistent_node() -> Result { ); let err = send.unwrap_err(); - assert_eq!(err.kind(), ErrorKind::Other); + assert_eq!(err.kind(), ErrorKind::HostUnreachable); assert_eq!(err.to_string(), "host unreachable"); Ok(())