use std::fmt;
use crate::{Effect, Instant, Name};
#[derive(Debug, PartialEq)]
pub enum Blocked {
Idle,
Sleeping { next_wakeup: Instant },
Deadlock(Vec<SendBlock>),
Breakpoint(Name, Effect),
Busy { external_effects: usize, stages: Vec<Name> },
Terminated(Name),
}
#[derive(Debug, PartialEq)]
pub struct SendBlock {
pub from: Name,
pub to: Name,
pub is_call: bool,
}
impl Blocked {
#[track_caller]
pub fn assert_idle(&self) {
match self {
Blocked::Idle => {}
_ => panic!("expected idle, got {:?}", self),
}
}
#[track_caller]
pub fn assert_sleeping(&self) -> Instant {
match self {
Blocked::Sleeping { next_wakeup } => *next_wakeup,
_ => panic!("expected sleeping, got {:?}", self),
}
}
#[track_caller]
pub fn assert_sleeping_until(&self, until: Instant) {
match self {
Blocked::Sleeping { next_wakeup } => {
assert_eq!(*next_wakeup, until);
}
_ => panic!("expected sleeping until {:?}, got {:?}", until, self),
}
}
#[track_caller]
pub fn assert_deadlock(&self, names: impl IntoIterator<Item = impl AsRef<str> + fmt::Debug>) {
let names = names.into_iter().collect::<Vec<_>>();
match self {
Blocked::Deadlock(deadlock)
if deadlock.iter().all(|send| names.iter().any(|n| name_match(&send.from, n.as_ref()))) => {}
_ => panic!("expected deadlock by {:?}, got {:?}", names, self),
}
}
#[track_caller]
pub fn assert_busy(&self, names: impl IntoIterator<Item = impl AsRef<str> + fmt::Debug>) -> &Self {
let names = names.into_iter().collect::<Vec<_>>();
match self {
Blocked::Busy { stages, .. } if names.iter().all(|n| stages.iter().any(|s| name_match(s, n.as_ref()))) => {}
_ => panic!("expected busy by {:?}, got {:?}", names, self),
}
self
}
#[track_caller]
pub fn assert_external_effects(&self, effects: usize) {
let Self::Busy { external_effects, .. } = self else {
panic!("expected busy state but got {:?}", self);
};
assert_eq!(*external_effects, effects);
}
#[track_caller]
pub fn assert_breakpoint(self, name: impl AsRef<str>) -> Effect {
match self {
Blocked::Breakpoint(n, eff) if name_match(&n, name.as_ref()) => eff,
_ => panic!("expected breakpoint `{}`, got {:?}", name.as_ref(), self),
}
}
#[track_caller]
pub fn assert_terminated(self, name: impl AsRef<str>) {
match self {
Blocked::Terminated(n) if name_match(&n, name.as_ref()) => {}
_ => panic!("expected terminated `{}`, got {:?}", name.as_ref(), self),
}
}
}
fn name_match(name: &Name, given: &str) -> bool {
let name = name.as_str();
if name == given {
return true;
}
if name.starts_with(given) && name.get(given.len()..).expect("`given` was valid UTF-8").starts_with('-') {
return name[given.len() + 1..].parse::<u64>().is_ok();
}
false
}