mod common;
use atap::{Runtime, RuntimeError, compute::Compute};
use common::settles;
use std::{
sync::{
Arc, Mutex,
atomic::{AtomicUsize, Ordering},
},
thread,
time::{Duration, Instant},
};
const PATIENCE: Duration = Duration::from_secs(10);
#[test]
fn a_compute_that_takes_nothing_runs_once() {
let _ = Runtime::init();
let answer = Runtime::task(Compute::compute(|()| 6 * 7)).spawn();
assert_eq!(answer.join_with_timeout(PATIENCE), Ok(42));
}
#[test]
fn blocking_on_a_compute_runs_it_here() {
let _ = Runtime::init();
let caller = thread::current().id();
let ran_on = Runtime::block(Compute::compute(|()| thread::current().id()));
assert_eq!(
ran_on, caller,
"a blocked on compute ran on some other thread"
);
}
#[test]
fn a_repeated_compute_runs_exactly_its_count() {
let _ = Runtime::init();
let runs = Arc::new(AtomicUsize::new(0));
let counted = Arc::clone(&runs);
let handle = Runtime::task(Compute::compute(move |()| {
counted.fetch_add(1, Ordering::Relaxed)
}))
.repeat()
.count(5)
.spawn();
assert!(
settles(|| handle.is_finished()),
"the series never finished"
);
assert_eq!(
runs.load(Ordering::Relaxed),
5,
"a count of five ran a different number of times"
);
}
#[test]
fn a_compute_on_a_rate_runs_its_count() {
let _ = Runtime::init();
let runs = Arc::new(AtomicUsize::new(0));
let counted = Arc::clone(&runs);
let handle = Runtime::task(Compute::compute(move |()| {
counted.fetch_add(1, Ordering::Relaxed);
}))
.at_rate(Duration::from_millis(10))
.count(4)
.spawn();
assert!(
settles(|| handle.is_finished() && runs.load(Ordering::Relaxed) == 4),
"a schedule of four ran {} times",
runs.load(Ordering::Relaxed),
);
}
#[test]
fn every_spaces_compute_runs_by_at_least_its_gap() {
let _ = Runtime::init();
let gap = Duration::from_millis(20);
let starts = Arc::new(Mutex::new(Vec::new()));
let noted = Arc::clone(&starts);
let handle = Runtime::task(Compute::compute(move |()| {
noted.lock().unwrap().push(Instant::now());
}))
.repeat()
.every(gap)
.count(3)
.spawn();
assert!(
settles(|| handle.is_finished()),
"the series never finished"
);
let starts = starts.lock().unwrap();
assert_eq!(
starts.len(),
3,
"a count of three ran {} times",
starts.len()
);
for pair in starts.windows(2) {
assert!(
pair[1] - pair[0] >= gap,
"two runs started {:?} apart, inside the {:?} gap",
pair[1] - pair[0],
gap,
);
}
}
#[test]
fn a_panicking_compute_fails_and_the_pool_carries_on() {
let _ = Runtime::init();
let doomed = Runtime::task(Compute::compute(|()| -> u8 {
panic!("this compute is meant to go down")
}))
.spawn();
assert_eq!(
doomed.join_with_timeout(PATIENCE),
Err(RuntimeError::TaskFailed)
);
let after = Runtime::task(Compute::compute(|()| 7u8)).spawn();
assert_eq!(
after.join_with_timeout(PATIENCE),
Ok(7),
"the pool stopped after a panic"
);
}
#[test]
fn an_output_bigger_than_a_slot_comes_back_whole() {
let _ = Runtime::init();
let handle = Runtime::task(Compute::compute(|()| {
let mut big = [0u64; 64];
for (index, cell) in big.iter_mut().enumerate() {
*cell = index as u64 * 31;
}
big
}))
.spawn();
let big = handle
.join_with_timeout(PATIENCE)
.expect("the big output never came");
assert!(
big.iter()
.enumerate()
.all(|(index, cell)| *cell == index as u64 * 31),
"the big output came back scrambled",
);
}
#[test]
fn a_compute_drops_what_it_captured_once_it_is_done() {
let _ = Runtime::init();
let held = Arc::new(());
let captured = Arc::clone(&held);
let handle = Runtime::task(Compute::compute(move |()| Arc::strong_count(&captured))).spawn();
let seen = handle
.join_with_timeout(PATIENCE)
.expect("the compute never ran");
assert!(
seen >= 2,
"the compute didn't hold its own claim while it ran"
);
assert!(
settles(|| Arc::strong_count(&held) == 1),
"the task kept what it captured after it was done",
);
}
#[test]
fn an_output_that_cannot_be_cloned_can_be_taken() {
let _ = Runtime::init();
struct Owned(Vec<u8>);
let handle = Runtime::task(Compute::compute(|()| Owned(vec![1, 2, 3]))).spawn();
let owned = handle
.take_with_timeout(PATIENCE)
.expect("the output never came");
assert_eq!(owned.0, vec![1, 2, 3]);
}
#[test]
fn a_blocking_compute_runs_to_its_end() {
let _ = Runtime::init();
let asked = Duration::from_millis(20);
let handle = Runtime::task(
Compute::compute(move |()| {
let started = Instant::now();
thread::sleep(asked);
started.elapsed()
})
.blocking(),
)
.spawn();
let slept = handle
.join_with_timeout(PATIENCE)
.expect("the blocking compute never came back");
assert!(
slept >= asked,
"a blocking compute came back after {:?} of {:?}",
slept,
asked
);
}
#[test]
fn a_compute_can_spawn_another_and_wait_for_it() {
let _ = Runtime::init();
let outer = Runtime::task(Compute::compute(|()| {
let inner = Runtime::task(Compute::compute(|()| 20)).spawn();
inner.join().expect("the inner compute never came back") + 1
}))
.spawn();
assert_eq!(outer.join_with_timeout(PATIENCE), Ok(21));
}
#[test]
fn a_delayed_compute_waits_out_its_delay() {
let _ = Runtime::init();
let delay = Duration::from_millis(30);
let spawned = Instant::now();
let handle = Runtime::task(Compute::compute(move |()| spawned.elapsed()))
.after(delay)
.spawn();
let waited = handle
.join_with_timeout(PATIENCE)
.expect("the delayed compute never ran");
assert!(
waited >= delay,
"a compute delayed {:?} ran after {:?}",
delay,
waited
);
}
#[test]
fn a_compute_cancelled_before_its_delay_never_runs() {
let _ = Runtime::init();
let runs = Arc::new(AtomicUsize::new(0));
let counted = Arc::clone(&runs);
let handle = Runtime::task(Compute::compute(move |()| {
counted.fetch_add(1, Ordering::Relaxed);
}))
.after(Duration::from_millis(100))
.spawn();
handle.clone().cancel();
thread::sleep(Duration::from_millis(200));
assert_eq!(
runs.load(Ordering::Relaxed),
0,
"a cancelled compute ran anyway"
);
assert_eq!(
handle.join_with_timeout(PATIENCE),
Err(RuntimeError::Cancelled)
);
}
#[test]
fn every_compute_comes_back_with_its_own_answer() {
let _ = Runtime::init();
let handles: Vec<_> = (0..10_000u64)
.map(|index| {
(
index,
Runtime::task(Compute::compute(move |()| index * index)).spawn(),
)
})
.collect();
for (index, handle) in handles {
assert_eq!(
handle.join_with_timeout(PATIENCE),
Ok(index * index),
"compute {} came back with another's answer",
index,
);
}
}