#![forbid(unsafe_code)]
use core::ops::Range;
use permit::{DeadlineExceeded, Permit, PermitRevoked};
use safina_async_test::async_test;
use std::time::{Duration, Instant};
fn expect_elapsed(before: Instant, range_ms: Range<u64>) -> Result<(), String> {
if range_ms.is_empty() {
return Err(format!("invalid range {range_ms:?}"));
}
let elapsed = before.elapsed();
let duration_range = Duration::from_millis(range_ms.start)..Duration::from_millis(range_ms.end);
if !duration_range.contains(&elapsed) {
return Err(format!(
"{elapsed:?} elapsed, out of range {duration_range:?}"
));
}
Ok(())
}
#[test]
fn new() {
let pmt = Permit::new();
assert!(!pmt.is_revoked());
assert_eq!(Some(()), pmt.ok());
pmt.revoke();
assert!(pmt.is_revoked());
assert_eq!(None, pmt.ok());
}
#[test]
fn debug() {
let pmt = Permit::default();
assert_eq!("Permit{revoked=false,num_subs=0}", &format!("{pmt:?}"));
let sub1 = pmt.new_sub();
let _sub2 = pmt.new_sub();
assert_eq!("Permit{revoked=false,num_subs=2}", &format!("{pmt:?}"));
assert_eq!("Permit{revoked=false,num_subs=0}", &format!("{sub1:?}"));
pmt.revoke();
assert_eq!("Permit{revoked=true,num_subs=2}", &format!("{pmt:?}"));
assert_eq!("Permit{revoked=true,num_subs=0}", &format!("{sub1:?}"));
}
#[test]
fn default() {
let pmt = Permit::default();
assert!(!pmt.is_revoked());
pmt.revoke();
assert!(pmt.is_revoked());
}
#[test]
fn revoke_superior() {
let superior = Permit::new();
let pmt = superior.new_sub();
let sub = pmt.new_sub();
assert!(!superior.is_revoked());
assert!(!pmt.is_revoked());
assert!(!sub.is_revoked());
superior.revoke();
assert!(superior.is_revoked());
assert!(pmt.is_revoked());
assert!(sub.is_revoked());
}
#[test]
fn revoke_sub() {
let pmt = Permit::new();
let sub = pmt.new_sub();
assert!(!pmt.is_revoked());
assert!(!sub.is_revoked());
sub.revoke();
assert!(!pmt.is_revoked());
assert!(sub.is_revoked());
}
#[test]
fn revoke_then_sub() {
let pmt = Permit::new();
pmt.revoke();
let sub = pmt.new_sub();
assert!(pmt.is_revoked());
assert!(sub.is_revoked());
}
#[test]
fn revoke_both() {
let pmt = Permit::new();
let sub = pmt.new_sub();
assert!(!pmt.is_revoked());
assert!(!sub.is_revoked());
sub.revoke();
pmt.revoke();
assert!(pmt.is_revoked());
assert!(sub.is_revoked());
}
#[test]
fn test_drop() {
let pmt = Permit::new();
let sub = pmt.new_sub();
drop(pmt);
assert!(sub.is_revoked());
}
#[test]
fn revoke_clone() {
let pmt = Permit::new();
let clone = pmt.clone();
clone.revoke();
assert!(!pmt.is_revoked());
assert!(clone.is_revoked());
}
#[test]
fn revoke_clone_source() {
let pmt = Permit::new();
let clone = pmt.clone();
pmt.revoke();
assert!(pmt.is_revoked());
assert!(!clone.is_revoked());
}
#[test]
fn revoke_clones_superior() {
let superior = Permit::new();
let pmt = superior.new_sub();
let clone = pmt.clone();
superior.revoke();
assert!(superior.is_revoked());
assert!(pmt.is_revoked());
assert!(clone.is_revoked());
}
#[test]
fn clone_revoked() {
let pmt = Permit::new();
pmt.revoke();
let clone = pmt.clone();
assert!(pmt.is_revoked());
assert!(clone.is_revoked());
}
#[test]
fn revoke_superior_and_clone() {
let superior = Permit::new();
let pmt = superior.new_sub();
superior.revoke();
let clone = pmt.clone();
assert!(superior.is_revoked());
assert!(pmt.is_revoked());
assert!(clone.is_revoked());
}
#[test]
fn sleep() {
let permit1 = Permit::new();
let permit2 = permit1.new_sub();
let permit3 = permit2.new_sub();
let before = Instant::now();
permit3.sleep(Duration::from_millis(50)).unwrap();
expect_elapsed(before, 50..100).unwrap();
let before = Instant::now();
std::thread::spawn(move || {
std::thread::sleep(Duration::from_millis(50));
drop(permit1);
});
let result = permit3.sleep(Duration::from_millis(100));
assert_eq!(Err(PermitRevoked), result);
expect_elapsed(before, 50..100).unwrap();
}
#[test]
fn sleep_until() {
let permit1 = Permit::new();
let permit2 = permit1.new_sub();
let before = Instant::now();
permit2
.sleep_until(before + Duration::from_millis(50))
.unwrap();
expect_elapsed(before, 50..100).unwrap();
let before = Instant::now();
std::thread::spawn(move || {
std::thread::sleep(Duration::from_millis(50));
drop(permit1);
});
let result = permit2.sleep_until(before + Duration::from_millis(100));
assert_eq!(Err(PermitRevoked), result);
expect_elapsed(before, 50..100).unwrap();
}
#[test]
fn sync_and_send() {
let superior = Permit::new();
let pmt = superior.new_sub();
let join_handle = std::thread::spawn(move || {
for _ in 0..50 {
if pmt.is_revoked() {
return true;
}
std::thread::sleep(Duration::from_millis(1));
}
false
});
superior.revoke();
assert!(join_handle.join().unwrap());
}
#[test]
fn has_subs() {
let before = Instant::now();
let permit = Permit::new();
assert!(!permit.has_subs());
let sub1 = permit.new_sub();
assert!(permit.has_subs());
drop(sub1);
assert!(!permit.has_subs());
for sleep_duration in [Duration::from_millis(50), Duration::from_millis(100)] {
let sub_permit = permit.new_sub();
std::thread::spawn(move || {
std::thread::sleep(sleep_duration);
drop(sub_permit);
});
}
assert!(permit.has_subs());
permit.wait_subs_timeout(Duration::from_secs(1)).unwrap();
expect_elapsed(before, 100..150).unwrap();
assert!(!permit.has_subs());
}
#[test]
fn wait_subs_timeout() {
let top_permit = Permit::new();
let permit = top_permit.new_sub();
std::thread::spawn(move || {
std::thread::sleep(Duration::from_millis(50));
drop(top_permit);
});
for sleep_duration in [Duration::from_millis(50), Duration::from_millis(150)] {
let sub_permit = permit.new_sub();
std::thread::spawn(move || {
std::thread::sleep(sleep_duration);
drop(sub_permit);
});
}
let before = Instant::now();
let result = permit.wait_subs_timeout(Duration::from_millis(100));
expect_elapsed(before, 100..150).unwrap();
assert_eq!(Err(DeadlineExceeded), result);
permit
.wait_subs_timeout(Duration::from_millis(100))
.unwrap();
expect_elapsed(before, 150..250).unwrap();
}
#[test]
fn wait_subs_deadline() {
let before = Instant::now();
let permit = Permit::new();
let sub_permit = permit.new_sub();
std::thread::spawn(move || {
std::thread::sleep(Duration::from_millis(100));
drop(sub_permit);
});
let deadline = before + Duration::from_millis(50);
assert_eq!(Err(DeadlineExceeded), permit.wait_subs_deadline(deadline));
expect_elapsed(before, 50..100).unwrap();
}
#[test]
fn deadline_exceeded() {
assert_eq!("DeadlineExceeded", &format!("{}", DeadlineExceeded {}));
}
#[test]
fn await_revoked_returns_immediately() {
let before = Instant::now();
let permit = Permit::new();
permit.revoke();
safina::executor::block_on(async move { permit.await });
expect_elapsed(before, 0..10).unwrap();
}
#[test]
fn await_timeout() {
safina::timer::start_timer_thread();
let permit = Permit::new();
let sub = permit.new_sub();
safina::executor::block_on(async move {
safina::timer::with_timeout(sub, Duration::from_millis(50))
.await
.unwrap_err()
});
}
#[test]
fn await_returns_when_revoked() {
let before = Instant::now();
let permit = Permit::new();
let sub = permit.new_sub();
std::thread::spawn(move || {
std::thread::sleep(Duration::from_millis(50));
drop(permit);
});
safina::executor::block_on(async move { sub.await });
expect_elapsed(before, 50..100).unwrap();
}
#[async_test]
async fn await_many() {
let before = Instant::now();
let top_permit = Permit::new();
let mut receivers = Vec::new();
for _ in 0..100_000 {
let permit = top_permit.new_sub();
let (sender, receiver) = safina::sync::oneshot();
receivers.push(receiver);
safina::executor::spawn(async move {
permit.await;
sender.send(()).unwrap();
});
}
safina::timer::sleep_for(Duration::from_millis(50)).await;
top_permit.revoke();
for mut receiver in receivers {
receiver.async_recv().await.unwrap();
}
expect_elapsed(before, 0..10_000).unwrap();
}
#[async_test]
async fn await_loop() {
let before = Instant::now();
let top_permit = Permit::new();
let permit = top_permit.new_sub();
for _ in 0..5 {
let sub = permit.new_sub();
safina::timer::with_timeout(sub, Duration::from_millis(1))
.await
.unwrap_err();
}
expect_elapsed(before, 0..500).unwrap();
drop(permit);
drop(top_permit);
}