From 9ea442addc91369c1ea1a7e7d69cdcbc0a4cf428 Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Thu, 14 Aug 2025 09:33:55 -0400 Subject: [PATCH 1/2] simln-lib/test: use sped up clock for deterministic defined events --- simln-lib/src/lib.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/simln-lib/src/lib.rs b/simln-lib/src/lib.rs index 3cd1a99e..9ed130e1 100755 --- a/simln-lib/src/lib.rs +++ b/simln-lib/src/lib.rs @@ -1592,7 +1592,7 @@ async fn track_payment_result( #[cfg(test)] mod tests { - use crate::clock::SystemClock; + use crate::clock::{Clock, SimulationClock, SystemClock}; use crate::test_utils::{MockLightningNode, TestNodesResult}; use crate::{ get_payment_delay, test_utils, test_utils::LightningTestNodeBuilder, LightningError, @@ -2031,20 +2031,20 @@ mod tests { let (shutdown_trigger, shutdown_listener) = triggered::trigger(); // Create simulation without a timeout. + let clock = Arc::new(SimulationClock::new(10).unwrap()); + let start = clock.now(); let simulation = Simulation::new( SimulationCfg::new(None, 100, 2.0, None, None), network.get_client_hashmap(), TaskTracker::new(), - Arc::new(SystemClock {}), + clock.clone(), shutdown_trigger, shutdown_listener, ); // Run the simulation - let start = std::time::Instant::now(); let _ = simulation.run(&vec![activity_1, activity_2]).await; - let elapsed = start.elapsed(); - + let elapsed = clock.now().duration_since(start).unwrap(); let expected_payment_list = vec![ network.nodes[1].pubkey, network.nodes[3].pubkey, @@ -2058,13 +2058,14 @@ mod tests { network.nodes[3].pubkey, ]; - // Check that simulation ran 20ish seconds because - // from activity_1 there are 5 payments with a wait_time of 2s -> 10s - // from activity_2 there are 5 payments with a wait_time of 4s -> 20s - // but the wait time is interleave between the payments. + // Check that simulation ran 20ish seconds because: + // - from activity_1 there are 5 payments with a wait_time of 2s -> 10s + // - from activity_2 there are 5 payments with a wait_time of 4s -> 20s + // - but the wait time is interleave between the payments. + // Since we're running with a sped up clock, we allow a little more leeway. assert!( - elapsed <= Duration::from_secs(21), - "Simulation should have run no more than 21, took {:?}", + elapsed <= Duration::from_secs(30), + "Simulation should have run no more than 30, took {:?}", elapsed ); From 90387e57477794cca089a29c476cc6a6d61c585d Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Thu, 14 Aug 2025 09:51:12 -0400 Subject: [PATCH 2/2] simln-lib/test: speed up random payment deterministic test clock Speed up this test by using our sim clock that can speed up the simulation. The downside of this clock is that we may stop one payment over/under if we try to match exactly to the number of payments that we expect in a given period of time. Instead, we generate our set of expected payment and then run the simulation for much longer than we need to get this expected list, so we should always get at least this subset. We then just assert that the payments we get exactly match this first set. --- simln-lib/src/lib.rs | 68 +++++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/simln-lib/src/lib.rs b/simln-lib/src/lib.rs index 9ed130e1..acdb3735 100755 --- a/simln-lib/src/lib.rs +++ b/simln-lib/src/lib.rs @@ -1592,7 +1592,7 @@ async fn track_payment_result( #[cfg(test)] mod tests { - use crate::clock::{Clock, SimulationClock, SystemClock}; + use crate::clock::{Clock, SimulationClock}; use crate::test_utils::{MockLightningNode, TestNodesResult}; use crate::{ get_payment_delay, test_utils, test_utils::LightningTestNodeBuilder, LightningError, @@ -2031,20 +2031,20 @@ mod tests { let (shutdown_trigger, shutdown_listener) = triggered::trigger(); // Create simulation without a timeout. - let clock = Arc::new(SimulationClock::new(10).unwrap()); - let start = clock.now(); + let clock = Arc::new(SimulationClock::new(10).unwrap()); + let start = clock.now(); let simulation = Simulation::new( SimulationCfg::new(None, 100, 2.0, None, None), network.get_client_hashmap(), TaskTracker::new(), - clock.clone(), + clock.clone(), shutdown_trigger, shutdown_listener, ); // Run the simulation let _ = simulation.run(&vec![activity_1, activity_2]).await; - let elapsed = clock.now().duration_since(start).unwrap(); + let elapsed = clock.now().duration_since(start).unwrap(); let expected_payment_list = vec![ network.nodes[1].pubkey, network.nodes[3].pubkey, @@ -2062,7 +2062,7 @@ mod tests { // - from activity_1 there are 5 payments with a wait_time of 2s -> 10s // - from activity_2 there are 5 payments with a wait_time of 4s -> 20s // - but the wait time is interleave between the payments. - // Since we're running with a sped up clock, we allow a little more leeway. + // Since we're running with a sped up clock, we allow a little more leeway. assert!( elapsed <= Duration::from_secs(30), "Simulation should have run no more than 30, took {:?}", @@ -2099,37 +2099,46 @@ mod tests { let (shutdown_trigger, shutdown_listener) = triggered::trigger(); - // Create simulation with a defined seed. + // Create simulation with a defined seed, and limit it to running for 45 seconds. + let clock = Arc::new(SimulationClock::new(20).unwrap()); let simulation = Simulation::new( - SimulationCfg::new(Some(25), 100, 2.0, None, Some(42)), + SimulationCfg::new(Some(45), 100, 2.0, None, Some(42)), network.get_client_hashmap(), TaskTracker::new(), - Arc::new(SystemClock {}), + clock.clone(), shutdown_trigger, shutdown_listener, ); - // Run the simulation - let start = std::time::Instant::now(); + let start = clock.now(); let _ = simulation.run(&[]).await; - let elapsed = start.elapsed(); + let elapsed = clock.now().duration_since(start).unwrap(); assert!( - elapsed >= Duration::from_secs(25), - "Simulation should have run at least for 25s, took {:?}", + elapsed >= Duration::from_secs(45), + "Simulation should have run at least for 45s, took {:?}", elapsed ); - let expected_payment_list = vec![ - pk1, pk2, pk1, pk1, pk1, pk3, pk3, pk3, pk4, pk3, pk2, pk1, pk4, - ]; - assert!( - payments_list.lock().unwrap().as_ref() == expected_payment_list, + // We're running with a sped up clock, so we're not going to hit exactly the same number + // of payments each time. We settle for asserting that our first 12 are deterministic. + // This ordering is set by running the simulation for 25 seconds, and we run for a total + // of 45 seconds so we can reasonably expect that we'll always get at least these 12 + // payments. + let expected_payment_list = + vec![pk1, pk2, pk1, pk1, pk1, pk3, pk3, pk3, pk4, pk3, pk3, pk1]; + let actual_payments: Vec = payments_list + .lock() + .unwrap() + .iter() + .cloned() + .take(12) + .collect(); + assert_eq!( + actual_payments, expected_payment_list, "The expected order of payments is not correct: {:?} vs {:?}", - payments_list.lock().unwrap(), - expected_payment_list, + actual_payments, expected_payment_list, ); - // remove all the payments made in the previous execution payments_list.lock().unwrap().clear(); @@ -2137,17 +2146,24 @@ mod tests { // Create the same simulation as before but with different seed. let simulation2 = Simulation::new( - SimulationCfg::new(Some(25), 100, 2.0, None, Some(500)), + SimulationCfg::new(Some(45), 100, 2.0, None, Some(500)), network.get_client_hashmap(), TaskTracker::new(), - Arc::new(SystemClock {}), + clock.clone(), shutdown_trigger, shutdown_listener, ); let _ = simulation2.run(&[]).await; - assert!( - payments_list.lock().unwrap().as_ref() != expected_payment_list, + let actual_payments: Vec = payments_list + .lock() + .unwrap() + .iter() + .cloned() + .take(12) + .collect(); + assert_ne!( + actual_payments, expected_payment_list, "The expected order of payments shoud be different because a different is used" ); }