#[cfg(test)]
mod tests {
use super::*;
use crate::coroutine::ProgressToken;
use crate::instr::Endpoint;
use crate::session::Edge;
#[test]
fn test_round_robin() {
let mut sched = Scheduler::new(SchedPolicy::Cooperative);
sched.add_ready(0);
sched.add_ready(1);
sched.add_ready(2);
assert_eq!(sched.schedule(), Some(0));
sched.reschedule(0);
assert_eq!(sched.schedule(), Some(1));
sched.reschedule(1);
assert_eq!(sched.schedule(), Some(2));
}
#[test]
fn test_block_unblock() {
let mut sched = Scheduler::new(SchedPolicy::Cooperative);
sched.add_ready(0);
sched.add_ready(1);
sched.mark_blocked(
0,
BlockReason::Recv {
edge: Edge::new(0, "B", "A"),
token: ProgressToken::for_endpoint(Endpoint {
sid: 0,
role: "A".into(),
}),
},
);
assert_eq!(sched.ready_count(), 1);
assert_eq!(sched.blocked_count(), 1);
sched.unblock(0);
assert_eq!(sched.ready_count(), 2);
assert_eq!(sched.blocked_count(), 0);
}
#[test]
fn test_progress_aware_tie_break_uses_ready_queue_order() {
let mut sched = Scheduler::new(SchedPolicy::ProgressAware);
sched.add_ready(3);
sched.add_ready(1);
sched.add_ready(2);
assert_eq!(sched.schedule_with(|id| id % 2 == 1), Some(3));
sched.reschedule(3);
assert_eq!(sched.schedule_with(|id| id % 2 == 1), Some(1));
}
#[test]
fn test_reschedule_moves_selected_coro_to_back_of_ready_queue() {
let mut sched = Scheduler::new(SchedPolicy::Cooperative);
sched.add_ready(0);
sched.add_ready(1);
assert_eq!(sched.schedule(), Some(0));
sched.reschedule(0);
assert_eq!(sched.ready_snapshot(), vec![1, 0]);
}
#[test]
fn test_priority_fixed_map_prefers_higher_weight() {
let mut priorities = BTreeMap::new();
priorities.insert(1, 10);
priorities.insert(2, 20);
priorities.insert(3, 5);
let mut sched = Scheduler::new(SchedPolicy::Priority(PriorityPolicy::FixedMap(priorities)));
sched.add_ready(1);
sched.add_ready(2);
sched.add_ready(3);
assert_eq!(sched.schedule(), Some(2));
}
#[test]
fn test_update_after_step_routes_blocked_to_blocked_set() {
let mut sched = Scheduler::new(SchedPolicy::RoundRobin);
sched.add_ready(7);
sched.update_after_step(
7,
StepUpdate::Blocked(BlockReason::Invoke {
handler: "default".to_string(),
}),
);
assert_eq!(sched.ready_count(), 0);
assert_eq!(sched.blocked_count(), 1);
assert!(matches!(
sched.block_reason(7),
Some(BlockReason::Invoke { .. })
));
}
#[test]
fn test_scheduler_state_serde_roundtrip_preserves_lane_views() {
let mut sched = Scheduler::new(SchedPolicy::ProgressAware);
sched.assign_lane(0, 0);
sched.assign_lane(1, 1);
sched.assign_lane(2, 0);
sched.assign_lane(3, 1);
sched.add_ready(0);
sched.add_ready(1);
sched.add_ready(2);
sched.add_ready(3);
sched.mark_blocked(
2,
BlockReason::Invoke {
handler: "io".to_string(),
},
);
sched.record_cross_lane_handoff(0, 1, "transfer 7:A");
let _ = sched.schedule_with(|id| id == 3);
let encoded = serde_json::to_string(&sched).expect("serialize scheduler");
let decoded: Scheduler = serde_json::from_str(&encoded).expect("deserialize scheduler");
assert_eq!(decoded.policy(), sched.policy());
assert_eq!(decoded.ready_snapshot(), sched.ready_snapshot());
assert_eq!(decoded.blocked_snapshot(), sched.blocked_snapshot());
assert_eq!(decoded.lane_queues_snapshot(), sched.lane_queues_snapshot());
assert_eq!(
decoded.lane_blocked_snapshot(),
sched.lane_blocked_snapshot()
);
assert_eq!(decoded.cross_lane_handoffs(), sched.cross_lane_handoffs());
assert_eq!(decoded.step_count(), sched.step_count());
assert_eq!(decoded.timeslice(), sched.timeslice());
}
#[test]
fn test_step_count_does_not_advance_when_no_pick() {
let mut sched = Scheduler::new(SchedPolicy::Cooperative);
assert_eq!(sched.step_count(), 0);
assert_eq!(sched.schedule(), None);
assert_eq!(sched.step_count(), 0);
}
#[test]
fn test_pick_runnable_from_set_skips_ineligible_ready_entries_without_reordering() {
let mut sched = Scheduler::new(SchedPolicy::Cooperative);
sched.add_ready(0);
sched.add_ready(1);
sched.add_ready(2);
sched.set_ready_eligibility(2, ReadyEligibility::Eligible);
assert_eq!(sched.pick_eligible_runnable(|_| false), Some(2));
assert_eq!(sched.ready_snapshot(), vec![0, 1]);
assert_eq!(sched.step_count(), 1);
}
}