use crate::{
Puck,
agent::Agent,
config::Config,
job::Job,
ops::defer,
simulator::{Prec, Sim, simulation},
};
use std::{
boxed::Box,
cell::Cell,
collections::VecDeque,
pin::{Pin, pin},
rc::Rc,
vec,
};
#[derive(Default)]
struct CustomConfig;
impl Config for CustomConfig {
type Time = i32;
type Rank = i32;
type Data = ();
type Plan = super::DefaultPlan<Self>;
fn default_time(&self) -> Self::Time {
0
}
fn default_rank(&self) -> Self::Rank {
0
}
fn global_data(&self) -> &Self::Data {
&()
}
}
struct Order(Cell<usize>);
impl Order {
const fn new() -> Self {
Self(Cell::new(0))
}
fn next(&self) -> usize {
let r = self.0.get();
self.0.set(r + 1);
r
}
}
#[test]
fn stable_ordering() {
async fn sim_main(sim: &Sim<CustomConfig>) {
let shared = Order::new();
let mut pool = (0..100)
.map(|_| Job::new(async { shared.next() }))
.collect::<Box<_>>();
let pucks = pool
.iter_mut()
.map(|job| sim.activate(unsafe { Pin::new_unchecked(job) }))
.collect::<Box<_>>();
for (order, puck) in pucks.into_iter().enumerate() {
assert_eq!(
puck.await,
order,
"Barring any other distinguishing factor, sorting of\
continuations is stable"
);
}
}
simulation(sim_main).unwrap();
}
#[test]
fn defer_passes_no_time() {
async fn sim_main(sim: &Sim<CustomConfig>) {
let now = sim.now();
sim.defer().await;
assert_eq!(now, sim.now(), "`defer` causes no model time to advance");
}
simulation(sim_main).unwrap();
}
#[test]
fn defer_skips_same_rank_jobs() {
async fn sim_main(sim: &Sim<CustomConfig>) {
let shared = Order::new();
let mut pool = (0..100)
.map(|_| Job::new(async { shared.next() }))
.collect::<Box<_>>();
for job in pool.iter_mut() {
sim.activate(unsafe { Pin::new_unchecked(job) });
}
sim.defer().await;
assert_eq!(
shared.next(),
100,
"`defer` causes all other continuations to be active first"
);
}
simulation(sim_main).unwrap();
}
#[test]
fn defer_skips_lower_rank_agents() {
async fn sim_main(sim: &Sim<CustomConfig>) {
let shared = Rc::new(Order::new());
let mut pool = (0..100)
.map(|_| {
Agent::build()
.with_rank(-1)
.with_subject(shared.clone())
.with_actions(async |shared: &Rc<Order>, _: &Sim<_>| shared.next())
.finish()
})
.collect::<Box<_>>();
for agent in pool.iter_mut() {
sim.activate(unsafe { Pin::new_unchecked(agent) });
}
sim.defer().await;
assert_eq!(
shared.next(),
100,
"`defer` causes all other continuations to be active first"
);
}
simulation(sim_main).unwrap();
}
#[test]
fn time_ordering() {
async fn sim_main(sim: &Sim<CustomConfig>) {
let mut pool = (0..100)
.map(|i| Job::new(async move { i }))
.collect::<Box<_>>();
let pucks = pool
.iter_mut()
.enumerate()
.map(|(i, job)| sim.schedule(unsafe { Pin::new_unchecked(job) }, i as i32))
.collect::<Box<_>>();
for puck in pucks {
assert_eq!(
puck.await,
sim.now(),
"Earlier continuations are processed before later ones"
);
}
}
simulation(sim_main).unwrap();
}
#[test]
fn agent_rank_ordering() {
async fn sim_main(sim: &Sim<CustomConfig>) {
let shared = Rc::new(Order::new());
let mut pool = (0..100)
.map(|i| {
Agent::build()
.with_subject(shared.clone())
.with_actions(async |i: &Rc<Order>, _: &Sim<_>| i.next())
.with_rank(i)
.finish()
})
.collect::<Box<_>>();
let pucks = pool
.iter_mut()
.map(|job| sim.activate(unsafe { Pin::new_unchecked(job) }))
.collect::<Box<_>>();
defer().await;
for (rank, puck) in pucks.into_iter().rev().enumerate() {
let result = puck.await;
assert_eq!(
rank, result,
"Agent's jobs are sorted according to their agent's rank \
in descending order"
);
}
}
simulation(sim_main).unwrap();
}
#[test]
fn job_precedence_linear_ordering() {
async fn sim_main(sim: &Sim<CustomConfig>) {
let shared = Order::new();
let mut pool = (0..100)
.map(|prec| {
Job::build()
.with_actions(async { shared.next() })
.with_precedence(Prec::from(prec))
.finish()
})
.collect::<Box<_>>();
let pucks = pool
.iter_mut()
.map(|job| sim.activate(unsafe { Pin::new_unchecked(job) }))
.collect::<Box<_>>();
defer().await;
for (prec, puck) in pucks.into_iter().enumerate() {
let result = puck.await;
assert_eq!(
prec, result,
"Jobs are scheduled according to their precedence in \
ascending order"
);
}
}
simulation(sim_main).unwrap();
}
#[test]
fn job_precedence_nested_ordering() {
async fn sim_main(sim: &Sim<CustomConfig>) {
let shared = Order::new();
let mut pool1 = vec![];
let mut pool2 = vec![];
let mut pool3 = vec![];
for prec1 in sim.active().prec().split(4).unwrap() {
pool1.push(
Job::build()
.with_actions(async { shared.next() })
.with_precedence(prec1)
.finish(),
);
for prec2 in prec1.split(3).unwrap() {
pool2.push(
Job::build()
.with_actions(async { shared.next() })
.with_precedence(prec2)
.finish(),
);
for prec3 in prec2.split(2).unwrap() {
pool3.push(
Job::build()
.with_actions(async { shared.next() })
.with_precedence(prec3)
.finish(),
);
}
}
}
let mut pucks1 = pool1
.iter_mut()
.map(|job| sim.activate(unsafe { Pin::new_unchecked(job) }))
.collect::<VecDeque<_>>();
let mut pucks2 = pool2
.iter_mut()
.map(|job| sim.activate(unsafe { Pin::new_unchecked(job) }))
.collect::<VecDeque<_>>();
let mut pucks3 = pool3
.iter_mut()
.map(|job| sim.activate(unsafe { Pin::new_unchecked(job) }))
.collect::<VecDeque<_>>();
defer().await;
let mut i = 0;
for _ in 0..4 {
assert_eq!(pucks1.pop_front().unwrap().await, i);
i += 1;
for _ in 0..3 {
assert_eq!(pucks2.pop_front().unwrap().await, i);
i += 1;
for _ in 0..2 {
assert_eq!(pucks3.pop_front().unwrap().await, i);
i += 1;
}
}
}
}
simulation(sim_main).unwrap();
}
#[test]
fn agent_mark_ordering() {
async fn sim_main(sim: &Sim<CustomConfig>) {
let shared = Rc::new(Order::new());
let mut pool = (0..4)
.map(|i| {
Agent::new((
(i, shared.clone()),
async |(i, shared): &(usize, Rc<Order>), sim: &Sim<_>| {
let mut pool = (0..3)
.map(|_| {
Job::new(async {
sim.advance(1).await;
shared.next()
})
})
.collect::<Box<_>>();
let mut pucks = vec![];
for job in pool.iter_mut() {
pucks.push(sim.activate(unsafe { Pin::new_unchecked(job) }));
sim.defer().await;
}
for (j, puck) in pucks.into_iter().enumerate() {
let order = puck.await;
assert_eq!(
order,
i * 3 + j,
"All jobs of agent A should be sorted before the \
jobs of agent B, if there is at least one job \
of A sorted before all jobs of B."
);
}
},
))
})
.collect::<Box<_>>();
for agent in pool.iter_mut() {
sim.activate(unsafe { Pin::new_unchecked(agent) });
}
sim.advance(2).await;
}
simulation(sim_main).unwrap();
}
#[test]
fn agent_rank_update() {
async fn sim_main(sim: &Sim<CustomConfig>) {
let shared = Rc::new(Order::new());
let smith = pin!(
Agent::build()
.with_rank(0)
.with_subject(shared.clone())
.with_actions(async |shared: &Rc<Order>, sim: &Sim<CustomConfig>| {
let j1 = pin!(Job::new(async { shared.next() }));
let p1 = sim.activate(j1);
sim.update_rank(1);
assert_eq!(p1.await, 0);
})
.finish()
);
let brown = pin!(
Agent::build()
.with_rank(1)
.with_subject(shared.clone())
.with_actions(async |shared: &Rc<Order>, sim: &Sim<CustomConfig>| {
let j1 = pin!(Job::new(async { shared.next() }));
let p1 = sim.activate(j1);
sim.update_rank(0);
assert_eq!(p1.await, 1);
})
.finish()
);
sim.activate(smith);
sim.activate(brown);
sim.advance(2).await;
}
simulation(sim_main).unwrap();
}