mod common;
use netrun_sim::graph::{Edge, PortRef, PortType};
use netrun_sim::net::{
Epoch, EpochState, NetAction, NetActionResponse, NetActionResponseData, NetEvent, NetSim,
PacketLocation, Salvo,
};
fn get_packet_id(response: &NetActionResponse) -> netrun_sim::net::PacketID {
match response {
NetActionResponse::Success(NetActionResponseData::Packet(id), _) => id.clone(),
_ => panic!("Expected Packet response, got: {:?}", response),
}
}
fn get_created_epoch(response: &NetActionResponse) -> Epoch {
match response {
NetActionResponse::Success(NetActionResponseData::CreatedEpoch(epoch), _) => epoch.clone(),
_ => panic!("Expected CreatedEpoch response, got: {:?}", response),
}
}
fn get_started_epoch(response: &NetActionResponse) -> Epoch {
match response {
NetActionResponse::Success(NetActionResponseData::StartedEpoch(epoch), _) => epoch.clone(),
_ => panic!("Expected StartedEpoch response, got: {:?}", response),
}
}
fn get_finished_epoch(response: &NetActionResponse) -> Epoch {
match response {
NetActionResponse::Success(NetActionResponseData::FinishedEpoch(epoch), _) => epoch.clone(),
_ => panic!("Expected FinishedEpoch response, got: {:?}", response),
}
}
fn get_events(response: &NetActionResponse) -> Vec<NetEvent> {
match response {
NetActionResponse::Success(_, events) => events.clone(),
_ => panic!("Expected Success response, got: {:?}", response),
}
}
fn make_edge(source_node: &str, source_port: &str, target_node: &str, target_port: &str) -> Edge {
Edge {
source: PortRef {
node_name: source_node.to_string(),
port_type: PortType::Output,
port_name: source_port.to_string(),
},
target: PortRef {
node_name: target_node.to_string(),
port_type: PortType::Input,
port_name: target_port.to_string(),
},
}
}
#[test]
fn test_complete_linear_flow_a_to_b() {
let graph = common::linear_graph_3();
let mut net = NetSim::new(graph);
let packet_id = get_packet_id(&net.do_action(&NetAction::CreatePacket(None)));
let edge_a_b = PacketLocation::Edge(make_edge("A", "out", "B", "in"));
net.do_action(&NetAction::TransportPacketToLocation(
packet_id.clone(),
edge_a_b,
));
let events = net.run_until_blocked();
assert!(
events
.iter()
.any(|e| matches!(e, NetEvent::EpochCreated(_, _)))
);
let epoch_ids = net.get_startable_epochs();
assert_eq!(epoch_ids.len(), 1);
let epoch = get_started_epoch(&net.do_action(&NetAction::StartEpoch(epoch_ids[0].clone())));
assert!(matches!(epoch.state, EpochState::Running));
assert_eq!(epoch.node_name, "B");
net.do_action(&NetAction::ConsumePacket(packet_id));
let finished =
get_finished_epoch(&net.do_action(&NetAction::FinishEpoch(epoch_ids[0].clone())));
assert!(matches!(finished.state, EpochState::Finished));
}
#[test]
fn test_linear_flow_with_output_salvo() {
let graph = common::linear_graph_3();
let mut net = NetSim::new(graph);
let input_packet_id = get_packet_id(&net.do_action(&NetAction::CreatePacket(None)));
let input_port_loc = PacketLocation::InputPort("B".to_string(), "in".to_string());
net.do_action(&NetAction::TransportPacketToLocation(
input_packet_id.clone(),
input_port_loc,
));
let salvo = Salvo {
salvo_condition: "manual".to_string(),
packets: vec![("in".to_string(), input_packet_id.clone())],
};
let epoch = get_created_epoch(&net.do_action(&NetAction::CreateEpoch("B".to_string(), salvo)));
let epoch = get_started_epoch(&net.do_action(&NetAction::StartEpoch(epoch.id)));
net.do_action(&NetAction::ConsumePacket(input_packet_id));
let output_packet_id =
get_packet_id(&net.do_action(&NetAction::CreatePacket(Some(epoch.id.clone()))));
net.do_action(&NetAction::LoadPacketIntoOutputPort(
output_packet_id.clone(),
"out".to_string(),
));
let response = net.do_action(&NetAction::SendOutputSalvo(
epoch.id.clone(),
"default".to_string(),
));
assert!(matches!(response, NetActionResponse::Success(_, _)));
let edge_b_c = PacketLocation::Edge(make_edge("B", "out", "C", "in"));
assert_eq!(net.packet_count_at(&edge_b_c), 1);
net.do_action(&NetAction::FinishEpoch(epoch.id));
}
#[test]
fn test_branching_flow() {
let graph = common::branching_graph();
let mut net = NetSim::new(graph);
let packet1 = get_packet_id(&net.do_action(&NetAction::CreatePacket(None)));
let packet2 = get_packet_id(&net.do_action(&NetAction::CreatePacket(None)));
let edge_a_b = PacketLocation::Edge(make_edge("A", "out1", "B", "in"));
let edge_a_c = PacketLocation::Edge(make_edge("A", "out2", "C", "in"));
net.do_action(&NetAction::TransportPacketToLocation(
packet1.clone(),
edge_a_b,
));
net.do_action(&NetAction::TransportPacketToLocation(
packet2.clone(),
edge_a_c,
));
net.run_until_blocked();
let epoch_ids = net.get_startable_epochs();
assert_eq!(epoch_ids.len(), 2);
let epoch_nodes: Vec<_> = epoch_ids
.iter()
.map(|id| net.get_epoch(id).unwrap().node_name.clone())
.collect();
assert!(epoch_nodes.contains(&"B".to_string()));
assert!(epoch_nodes.contains(&"C".to_string()));
}
#[test]
fn test_merging_flow_both_inputs_required() {
let graph = common::merging_graph();
let mut net = NetSim::new(graph);
let packet1 = get_packet_id(&net.do_action(&NetAction::CreatePacket(None)));
let edge_a_c = PacketLocation::Edge(make_edge("A", "out", "C", "in1"));
net.do_action(&NetAction::TransportPacketToLocation(
packet1.clone(),
edge_a_c,
));
net.run_until_blocked();
assert_eq!(net.get_startable_epochs().len(), 0);
let packet2 = get_packet_id(&net.do_action(&NetAction::CreatePacket(None)));
let edge_b_c = PacketLocation::Edge(make_edge("B", "out", "C", "in2"));
net.do_action(&NetAction::TransportPacketToLocation(
packet2.clone(),
edge_b_c,
));
net.run_until_blocked();
let epoch_ids = net.get_startable_epochs();
assert_eq!(epoch_ids.len(), 1);
assert_eq!(net.get_epoch(&epoch_ids[0]).unwrap().node_name, "C");
}
#[test]
fn test_diamond_flow_synchronization() {
let graph = common::diamond_graph();
let mut net = NetSim::new(graph);
let packet1 = get_packet_id(&net.do_action(&NetAction::CreatePacket(None)));
let packet2 = get_packet_id(&net.do_action(&NetAction::CreatePacket(None)));
let edge_a_b = PacketLocation::Edge(make_edge("A", "out1", "B", "in"));
let edge_a_c = PacketLocation::Edge(make_edge("A", "out2", "C", "in"));
net.do_action(&NetAction::TransportPacketToLocation(
packet1.clone(),
edge_a_b,
));
net.do_action(&NetAction::TransportPacketToLocation(
packet2.clone(),
edge_a_c,
));
net.run_until_blocked();
let epoch_ids = net.get_startable_epochs();
assert_eq!(epoch_ids.len(), 2);
let epoch_nodes: Vec<_> = epoch_ids
.iter()
.map(|id| net.get_epoch(id).unwrap().node_name.clone())
.collect();
assert!(epoch_nodes.contains(&"B".to_string()));
assert!(epoch_nodes.contains(&"C".to_string()));
assert!(!epoch_nodes.contains(&"D".to_string()));
}
#[test]
fn test_cancel_epoch_workflow() {
let graph = common::linear_graph_3();
let mut net = NetSim::new(graph);
let packet_id = get_packet_id(&net.do_action(&NetAction::CreatePacket(None)));
let input_port_loc = PacketLocation::InputPort("B".to_string(), "in".to_string());
net.do_action(&NetAction::TransportPacketToLocation(
packet_id.clone(),
input_port_loc,
));
let salvo = Salvo {
salvo_condition: "manual".to_string(),
packets: vec![("in".to_string(), packet_id.clone())],
};
let epoch = get_created_epoch(&net.do_action(&NetAction::CreateEpoch("B".to_string(), salvo)));
let epoch = get_started_epoch(&net.do_action(&NetAction::StartEpoch(epoch.id)));
let response = net.do_action(&NetAction::CancelEpoch(epoch.id));
match response {
NetActionResponse::Success(
NetActionResponseData::CancelledEpoch(cancelled, destroyed),
events,
) => {
assert_eq!(cancelled.id, epoch.id);
assert_eq!(destroyed.len(), 1);
assert_eq!(destroyed[0], packet_id);
assert!(
events
.iter()
.any(|e| matches!(e, NetEvent::PacketDestroyed(_, _, _)))
);
assert!(
events
.iter()
.any(|e| matches!(e, NetEvent::EpochCancelled(_, _)))
);
}
_ => panic!("Expected CancelledEpoch response"),
}
assert!(net.get_packet(&packet_id).is_none());
}
#[test]
fn test_multiple_sequential_epochs_on_same_node() {
let graph = common::linear_graph_3();
let mut net = NetSim::new(graph);
let packet1 = get_packet_id(&net.do_action(&NetAction::CreatePacket(None)));
let input_port_loc = PacketLocation::InputPort("B".to_string(), "in".to_string());
net.do_action(&NetAction::TransportPacketToLocation(
packet1.clone(),
input_port_loc.clone(),
));
let salvo1 = Salvo {
salvo_condition: "manual".to_string(),
packets: vec![("in".to_string(), packet1.clone())],
};
let epoch1 =
get_created_epoch(&net.do_action(&NetAction::CreateEpoch("B".to_string(), salvo1)));
let epoch1 = get_started_epoch(&net.do_action(&NetAction::StartEpoch(epoch1.id)));
net.do_action(&NetAction::ConsumePacket(packet1));
net.do_action(&NetAction::FinishEpoch(epoch1.id));
let packet2 = get_packet_id(&net.do_action(&NetAction::CreatePacket(None)));
net.do_action(&NetAction::TransportPacketToLocation(
packet2.clone(),
input_port_loc,
));
let salvo2 = Salvo {
salvo_condition: "manual".to_string(),
packets: vec![("in".to_string(), packet2.clone())],
};
let epoch2 =
get_created_epoch(&net.do_action(&NetAction::CreateEpoch("B".to_string(), salvo2)));
let epoch2 = get_started_epoch(&net.do_action(&NetAction::StartEpoch(epoch2.id)));
assert_eq!(epoch2.node_name, "B");
assert_ne!(epoch1.id, epoch2.id);
net.do_action(&NetAction::ConsumePacket(packet2));
net.do_action(&NetAction::FinishEpoch(epoch2.id));
}
#[test]
fn test_packet_location_tracking() {
let graph = common::linear_graph_3();
let mut net = NetSim::new(graph);
let packet_id = get_packet_id(&net.do_action(&NetAction::CreatePacket(None)));
let packet = net.get_packet(&packet_id).unwrap();
assert_eq!(packet.location, PacketLocation::OutsideNet);
let edge_loc = PacketLocation::Edge(make_edge("A", "out", "B", "in"));
net.do_action(&NetAction::TransportPacketToLocation(
packet_id.clone(),
edge_loc.clone(),
));
let packet = net.get_packet(&packet_id).unwrap();
assert_eq!(packet.location, edge_loc);
assert_eq!(net.packet_count_at(&edge_loc), 1);
assert_eq!(net.packet_count_at(&PacketLocation::OutsideNet), 0);
}
#[test]
fn test_get_packets_at_location() {
let graph = common::linear_graph_3();
let mut net = NetSim::new(graph);
let packet1 = get_packet_id(&net.do_action(&NetAction::CreatePacket(None)));
let packet2 = get_packet_id(&net.do_action(&NetAction::CreatePacket(None)));
let outside_packets = net.get_packets_at_location(&PacketLocation::OutsideNet);
assert_eq!(outside_packets.len(), 2);
assert!(outside_packets.contains(&packet1));
assert!(outside_packets.contains(&packet2));
let edge_loc = PacketLocation::Edge(make_edge("A", "out", "B", "in"));
net.do_action(&NetAction::TransportPacketToLocation(
packet1.clone(),
edge_loc.clone(),
));
let outside_packets = net.get_packets_at_location(&PacketLocation::OutsideNet);
assert_eq!(outside_packets.len(), 1);
assert!(outside_packets.contains(&packet2));
let edge_packets = net.get_packets_at_location(&edge_loc);
assert_eq!(edge_packets.len(), 1);
assert!(edge_packets.contains(&packet1));
}