mod common;
use atap::{Runtime, RuntimeError, channel::Channel, compute::Compute};
use common::until_started;
use std::{
collections::HashSet,
sync::mpsc,
thread,
time::{Duration, Instant},
};
const PATIENCE: Duration = Duration::from_secs(10);
#[test]
fn values_arrive_in_order() {
let _ = Runtime::init();
let (tx, rx) = Channel::new::<u32>().open().unwrap();
for value in 0..100 {
tx.send(value).unwrap();
}
assert_eq!(tx.len(), 100);
for value in 0..100 {
assert_eq!(Runtime::block(rx.recv()), Ok(value));
}
assert!(rx.is_empty());
}
#[test]
fn a_spawned_receive_waits_for_a_value() {
let _ = Runtime::init();
let (tx, rx) = Channel::new::<String>().open().unwrap();
let waiting = Runtime::task(rx.recv()).spawn();
until_started(&waiting, PATIENCE);
assert!(
waiting.is_running(),
"a receive with nothing to take finished"
);
tx.send(String::from("late")).unwrap();
assert_eq!(
waiting.join_with_timeout(PATIENCE),
Ok(Ok(String::from("late")))
);
}
#[test]
fn try_recv_does_not_wait() {
let _ = Runtime::init();
let (tx, rx) = Channel::new::<u8>().open().unwrap();
assert_eq!(rx.try_recv(), Err(RuntimeError::NotReady));
tx.send(9).unwrap();
assert_eq!(rx.try_recv(), Ok(9));
}
#[test]
fn many_senders_lose_nothing() {
let _ = Runtime::init();
let (tx, rx) = Channel::new::<u64>().open().unwrap();
let threads: Vec<_> = (0..8u64)
.map(|sender| {
let tx = tx.clone();
thread::spawn(move || {
for at in 0..500 {
tx.send(sender * 1000 + at).unwrap();
}
})
})
.collect();
drop(tx);
let mut seen = HashSet::new();
loop {
match Runtime::task(rx.recv()).spawn().join_with_timeout(PATIENCE) {
Ok(Ok(value)) => assert!(seen.insert(value), "{value} came out twice"),
Ok(Err(RuntimeError::Closed)) => break,
other => panic!("a receive gave {other:?}"),
}
}
for thread in threads {
thread.join().unwrap();
}
assert_eq!(seen.len(), 4000);
}
#[test]
fn many_receivers_share_the_values() {
let _ = Runtime::init();
let (tx, rx) = Channel::new::<u64>().open().unwrap();
let takers: Vec<_> = (0..6)
.map(|_| {
let rx = rx.clone();
thread::spawn(move || {
let mut got = Vec::new();
while let Ok(value) = Runtime::block(rx.recv()) {
got.push(value);
}
got
})
})
.collect();
drop(rx);
for value in 0..3000 {
tx.send(value).unwrap();
}
drop(tx);
let mut all: Vec<u64> = takers
.into_iter()
.flat_map(|taker| taker.join().unwrap())
.collect();
all.sort_unstable();
assert_eq!(all, (0..3000).collect::<Vec<_>>());
}
#[test]
fn a_closed_channel_drains_then_closes() {
let _ = Runtime::init();
let (tx, rx) = Channel::new::<u8>().open().unwrap();
tx.send(1).unwrap();
tx.send(2).unwrap();
drop(tx);
assert_eq!(Runtime::block(rx.recv()), Ok(1));
assert_eq!(Runtime::block(rx.recv()), Ok(2));
assert_eq!(Runtime::block(rx.recv()), Err(RuntimeError::Closed));
assert_eq!(rx.try_recv(), Err(RuntimeError::Closed));
}
#[test]
fn a_waiting_receive_hears_the_close() {
let _ = Runtime::init();
let (tx, rx) = Channel::new::<u8>().open().unwrap();
let waiting = Runtime::task(rx.recv()).spawn();
until_started(&waiting, PATIENCE);
drop(tx);
assert_eq!(
waiting.join_with_timeout(PATIENCE),
Ok(Err(RuntimeError::Closed))
);
}
#[test]
fn sending_to_nobody_is_closed() {
let _ = Runtime::init();
let (tx, rx) = Channel::new::<u8>().open().unwrap();
drop(rx);
assert_eq!(tx.send(1), Err(RuntimeError::Closed));
let (tx, rx) = Channel::new::<u8>().bounded(4).open().unwrap();
drop(rx);
assert_eq!(Runtime::block(tx.send(1)), Err(RuntimeError::Closed));
assert_eq!(tx.try_send(1), Err(RuntimeError::Closed));
}
#[test]
fn a_full_channel_makes_a_send_wait() {
let _ = Runtime::init();
let (tx, rx) = Channel::new::<u8>().bounded(2).open().unwrap();
Runtime::block(tx.send(1)).unwrap();
Runtime::block(tx.send(2)).unwrap();
assert_eq!(tx.try_send(3), Ok(Err(3)));
let waiting = Runtime::task(tx.send(3)).spawn();
until_started(&waiting, PATIENCE);
assert!(waiting.is_running(), "a send into a full channel finished");
assert_eq!(Runtime::block(rx.recv()), Ok(1));
assert_eq!(waiting.join_with_timeout(PATIENCE), Ok(Ok(())));
assert_eq!(Runtime::block(rx.recv()), Ok(2));
assert_eq!(Runtime::block(rx.recv()), Ok(3));
}
#[test]
fn a_waiting_send_hears_the_close() {
let _ = Runtime::init();
let (tx, rx) = Channel::new::<u8>().bounded(1).open().unwrap();
Runtime::block(tx.send(1)).unwrap();
let waiting = Runtime::task(tx.send(2)).spawn();
until_started(&waiting, PATIENCE);
drop(rx);
assert_eq!(
waiting.join_with_timeout(PATIENCE),
Ok(Err(RuntimeError::Closed))
);
}
#[test]
fn a_zero_capacity_holds_one() {
let _ = Runtime::init();
let (tx, rx) = Channel::new::<u8>().bounded(0).open().unwrap();
assert_eq!(tx.try_send(1), Ok(Ok(())));
assert_eq!(tx.try_send(2), Ok(Err(2)));
assert_eq!(rx.try_recv(), Ok(1));
}
#[test]
fn a_send_task_sends_once() {
let _ = Runtime::init();
let (tx, rx) = Channel::new::<u8>().bounded(4).open().unwrap();
let send = tx.send(5);
assert_eq!(Runtime::block(send.clone()), Ok(()));
assert_eq!(Runtime::block(send), Err(RuntimeError::Finished));
assert_eq!(rx.len(), 1);
}
#[test]
fn a_cancelled_receive_takes_nothing() {
let _ = Runtime::init();
let (tx, rx) = Channel::new::<u8>().open().unwrap();
let waiting = Runtime::task(rx.recv()).spawn();
until_started(&waiting, PATIENCE);
waiting.clone().cancel();
assert_eq!(
waiting.join_with_timeout(PATIENCE),
Err(RuntimeError::Cancelled)
);
tx.send(4).unwrap();
assert_eq!(Runtime::block(rx.recv()), Ok(4));
}
#[test]
fn a_receive_can_time_out() {
let _ = Runtime::init();
let (tx, rx) = Channel::new::<u8>().open().unwrap();
let started = Instant::now();
let waiting = Runtime::task(rx.recv())
.timeout(Duration::from_millis(50))
.spawn();
assert_eq!(
waiting.join_with_timeout(PATIENCE),
Err(RuntimeError::TimedOut)
);
assert!(started.elapsed() >= Duration::from_millis(50));
tx.send(8).unwrap();
assert_eq!(Runtime::block(rx.recv()), Ok(8));
}
#[test]
fn a_repeating_receive_passes_every_value_on() {
let _ = Runtime::init();
let (tx, rx) = Channel::new::<u32>().open().unwrap();
let (seen, heard) = mpsc::channel();
let collector = Runtime::task(Compute::compute(move |got: Result<u32, RuntimeError>| {
let _ = seen.send(got);
}))
.wait_for::<Result<u32, RuntimeError>>()
.spawn();
let _reader = Runtime::task(rx.recv())
.repeat()
.count(50)
.give_to(&collector)
.spawn();
for value in 0..50 {
tx.send(value).unwrap();
assert_eq!(heard.recv_timeout(PATIENCE), Ok(Ok(value)));
}
}