use curfew::{join_with_deadline, DeadlineJoiner, JoinOutcome, TaskOutcome};
use std::thread;
use std::time::{Duration, Instant};
#[test]
fn collects_results_before_deadline() {
let deadline = Instant::now() + Duration::from_millis(200);
let mut joiner = DeadlineJoiner::new(deadline);
for value in 0..3u32 {
joiner.spawn(move || {
thread::sleep(Duration::from_millis(40));
value * 2
});
}
let summary = joiner.join_ready();
assert_eq!(summary.completed_count(), 3);
assert_eq!(summary.timed_out_count(), 0);
let mut outputs: Vec<_> = summary
.completed
.into_iter()
.map(|result| match result.outcome {
TaskOutcome::Completed(v) => v,
TaskOutcome::Panicked(info) => panic!("unexpected panic: {info}"),
})
.collect();
outputs.sort_unstable();
assert_eq!(outputs, vec![0, 2, 4]);
}
#[test]
fn deadline_skips_late_tasks() {
let deadline = Instant::now() + Duration::from_millis(150);
let mut joiner = DeadlineJoiner::new(deadline);
joiner.spawn(|| {
thread::sleep(Duration::from_millis(50));
10
});
joiner.spawn(|| {
thread::sleep(Duration::from_millis(200));
20
});
joiner.spawn(|| {
thread::sleep(Duration::from_millis(210));
30
});
let summary = joiner.join_ready();
assert_eq!(summary.completed_count(), 1);
assert_eq!(summary.timed_out_count(), 2);
}
#[test]
fn propagates_panics_as_metadata() {
let deadline = Instant::now() + Duration::from_millis(150);
let mut joiner = DeadlineJoiner::new(deadline);
joiner.spawn(|| {
thread::sleep(Duration::from_millis(20));
1
});
joiner.spawn(|| panic!("boom"));
let summary = joiner.join_ready();
assert_eq!(summary.completed_count(), 2);
let mut saw_panic = false;
for result in summary.completed {
match result.outcome {
TaskOutcome::Completed(_) => {}
TaskOutcome::Panicked(info) => {
saw_panic = true;
assert!(info.message().contains("boom"));
}
}
}
assert!(saw_panic, "expected at least one panic to be recorded");
}
#[test]
fn join_with_deadline_finishes_in_time() {
let handle = thread::spawn(|| {
thread::sleep(Duration::from_millis(20));
42
});
let outcome = join_with_deadline(
handle,
Instant::now() + Duration::from_millis(200),
);
match outcome {
JoinOutcome::Completed(result) => match result.outcome {
TaskOutcome::Completed(value) => assert_eq!(value, 42),
TaskOutcome::Panicked(info) => panic!("unexpected panic: {info}"),
},
JoinOutcome::TimedOut(_) => {
panic!("thread should have completed in time")
}
}
}
#[test]
fn join_with_deadline_can_recover_late_result() {
let handle = thread::spawn(|| {
thread::sleep(Duration::from_millis(150));
99
});
let deadline = Instant::now() + Duration::from_millis(50);
match join_with_deadline(handle, deadline) {
JoinOutcome::Completed(_) => {
panic!("thread should not have met the deadline")
}
JoinOutcome::TimedOut(late) => {
let result = late.wait();
match result.outcome {
TaskOutcome::Completed(value) => assert_eq!(value, 99),
TaskOutcome::Panicked(info) => {
panic!("unexpected panic: {info}")
}
}
}
}
}