use std::collections::HashMap; use std::time::Duration; use sugars::boxed; use anysystem::logger::LogEntry; use anysystem::mc::{ predicates::{goals, invariants, prunes}, strategies::Bfs, InvariantFn, ModelChecker, StrategyConfig, }; use anysystem::test::TestResult; use anysystem::Message; use crate::common::{ build_system, check_delivered_messages, check_message_delivery_once, check_message_delivery_ordered, check_message_delivery_reliable, generate_message_texts, TestConfig, }; fn mc_invariant_guarantees(messages_expected: Vec, config: TestConfig) -> InvariantFn { boxed!(move |state| { let mut expected_msg_count = HashMap::new(); for msg in &messages_expected { *expected_msg_count.entry(msg.data.clone()).or_insert(0) += 1; } let delivered = &state.node_states["receiver-node"].proc_states["receiver"].local_outbox; // check that delivered messages have expected type and data let delivered_msg_count = check_delivered_messages(delivered, &expected_msg_count, &messages_expected[0].tip)?; // check delivered message count according to expected guarantees if config.reliable && state.events.is_empty() { check_message_delivery_reliable(&delivered_msg_count, &expected_msg_count)?; } if config.once { check_message_delivery_once(&delivered_msg_count, &expected_msg_count)?; } if config.ordered { check_message_delivery_ordered(delivered, &messages_expected)?; } Ok(()) }) } pub fn test_mc_reliable_network(config: &TestConfig) -> TestResult { let mut sys = build_system(config, false); let messages: Vec = generate_message_texts(&mut sys, 2) .into_iter() .map(|text| Message::new("MESSAGE", &format!(r#"{{"text": "{text}"}}"#))) .collect(); let strategy_config = StrategyConfig::default() .prune(prunes::sent_messages_limit(4)) .goal(goals::got_n_local_messages("receiver-node", "receiver", 2)) .invariant(invariants::all_invariants(vec![ invariants::state_depth(20), mc_invariant_guarantees(messages.clone(), *config), ])); let mut mc = ModelChecker::new(&sys); let res = mc.run_with_change::(strategy_config, move |sys| { for message in messages { sys.send_local_message("sender-node", "sender", message); } }); if let Err(e) = res { e.print_trace(); Err(e.message()) } else { Ok(true) } } pub fn test_mc_message_drops(config: &TestConfig) -> TestResult { let mut sys = build_system(config, false); sys.network().set_drop_rate(0.1); let messages: Vec = generate_message_texts(&mut sys, 2) .into_iter() .map(|text| Message::new("MESSAGE", &format!(r#"{{"text": "{text}"}}"#))) .collect(); let strategy_config = StrategyConfig::default() .prune(prunes::state_depth(7)) .goal(goals::any_goal(vec![ goals::got_n_local_messages("receiver-node", "receiver", 2), goals::no_events(), ])) .invariant(mc_invariant_guarantees(messages.clone(), *config)); let mut mc = ModelChecker::new(&sys); let res = mc.run_with_change::(strategy_config, move |sys| { for message in messages { sys.send_local_message("sender-node", "sender", message); } }); if let Err(e) = res { e.print_trace(); Err(e.message()) } else { Ok(true) } } pub fn test_mc_unstable_network(config: &TestConfig) -> TestResult { let mut sys = build_system(config, false); sys.network().set_drop_rate(0.1); sys.network().set_dupl_rate(0.1); let msg_count = if config.ordered { 3 } else { 2 }; let messages: Vec = generate_message_texts(&mut sys, msg_count) .into_iter() .map(|text| Message::new("MESSAGE", &format!(r#"{{"text": "{text}"}}"#))) .collect(); let num_drops_allowed = 1; let num_duplication_allowed = 1; let goal = if config.reliable && config.once { goals::all_goals(vec![ goals::got_n_local_messages("receiver-node", "receiver", msg_count), goals::no_events(), ]) } else { goals::no_events() }; let mut invariants = vec![ invariants::state_depth(20), mc_invariant_guarantees(messages.clone(), *config), ]; if config.ordered { invariants.push(invariants::time_limit(Duration::from_secs(80))) }; let strategy_config = StrategyConfig::default() .prune(prunes::any_prune(vec![ prunes::events_limit(LogEntry::is_mc_message_dropped, num_drops_allowed), prunes::events_limit(LogEntry::is_mc_message_duplicated, num_duplication_allowed), prunes::events_limit(LogEntry::is_mc_timer_fired, 1), prunes::events_limit(LogEntry::is_mc_message_received, msg_count + num_drops_allowed), ])) .goal(goal) .invariant(invariants::all_invariants(invariants)); let mut mc = ModelChecker::new(&sys); let res = mc.run_with_change::(strategy_config, |sys| { for msg in messages { sys.send_local_message("sender-node", "sender", msg.clone()); } }); if let Err(e) = res { e.print_trace(); Err(e.message()) } else { Ok(true) } }