aboutsummaryrefslogtreecommitdiffstats
path: root/src/simtest
diff options
context:
space:
mode:
Diffstat (limited to 'src/simtest')
-rw-r--r--src/simtest/identity.rs1
-rw-r--r--src/simtest/mod.rs1
-rw-r--r--src/simtest/reach.rs52
-rw-r--r--src/simtest/timing.rs1
4 files changed, 55 insertions, 0 deletions
diff --git a/src/simtest/identity.rs b/src/simtest/identity.rs
index 7379048..154c72e 100644
--- a/src/simtest/identity.rs
+++ b/src/simtest/identity.rs
@@ -112,6 +112,7 @@ fn status(node: &AppState) -> ParsedMessage {
leaving: false,
announcing: false,
eager: false,
+ reaches: None,
})
}
diff --git a/src/simtest/mod.rs b/src/simtest/mod.rs
index dc23440..e9d8c15 100644
--- a/src/simtest/mod.rs
+++ b/src/simtest/mod.rs
@@ -13,6 +13,7 @@ mod bench;
mod deltas;
mod identity;
mod links;
+mod reach;
mod relay;
mod remakes;
mod soak;
diff --git a/src/simtest/reach.rs b/src/simtest/reach.rs
new file mode 100644
index 0000000..b38db87
--- /dev/null
+++ b/src/simtest/reach.rs
@@ -0,0 +1,52 @@
+//! Whom neighbours reach (`swarm::reach`): told in STATUSes, as measured.
+
+use std::time::Duration;
+
+use super::Sim;
+use crate::state::AppState;
+use crate::swarm::bootstrap::Start;
+use crate::swarm::{reach, topology};
+
+const SOON: Duration = Duration::from_secs(30);
+
+/// Whether `node` reaches `member`, as `node` measured and told it.
+fn reaches(node: &AppState, member: &AppState) -> bool {
+ reach::published(node).contains(&topology::pair_key(member.local_id()))
+}
+
+/// Whether `node` heard from `neighbour` that it reaches `member`.
+async fn heard(node: &AppState, neighbour: &AppState, member: &AppState) -> bool {
+ let Some(peer_state) = node.get_peer_state(neighbour.local_id()).await else {
+ return false;
+ };
+ let said = peer_state.lock().await.reaches.clone();
+ reach::of(node, said.as_ref())
+ .is_some_and(|keys| keys.contains(&topology::pair_key(member.local_id())))
+}
+
+/// x, b and c on one net, all members of one full mesh, but nothing passes between x
+/// and c: b reaches both and says so; c learns from b that b reaches x, which c itself
+/// doesn't.
+#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
+async fn neighbours_tell_whom_they_reach() {
+ let sim = Sim::with_identity(66);
+ let cut = crate::transport::sim::LinkParams {
+ loss: 1.0,
+ ..Default::default()
+ };
+ sim.net.set_link("x", "c", cut.clone());
+ sim.net.set_link("c", "x", cut);
+ let b = sim.start_on("b", &["sim"], &[], Start::Genesis);
+ let c = sim.start_on("c", &["sim"], &["b"], Start::Join);
+ let x = sim.start_on("x", &["sim"], &["b"], Start::Join);
+ sim.wait_ready(&[&b, &c, &x], SOON).await;
+ let (b, c, x) = (&b, &c, &x);
+ sim.wait_until(
+ "c heard from b that b reaches x, and c doesn't reach x",
+ SOON,
+ move || async move {
+ reaches(b, x) && reaches(b, c) && !reaches(c, x) && heard(c, b, x).await
+ },
+ )
+ .await;
+}
diff --git a/src/simtest/timing.rs b/src/simtest/timing.rs
index ea59689..d06e992 100644
--- a/src/simtest/timing.rs
+++ b/src/simtest/timing.rs
@@ -337,6 +337,7 @@ async fn late_epoch_does_not_flip_a_peers_epoch() {
leaving: false,
announcing: false,
eager: false,
+ reaches: None,
})
};
let a = &a;