use crate::{SuspendUnawareInstant, TimedOutError, NANOS_PER_SECOND};
use futures::future::join_all;
use std::{
cmp::Ordering,
time::{Duration, Instant},
};
const TOLERANCE_MS: u64 = 1000;
const TOLERANCE_MS_U128: u128 = TOLERANCE_MS as u128;
fn create_instant(secs: u64, nanos: u32) -> SuspendUnawareInstant {
SuspendUnawareInstant { secs, nanos }
}
#[test]
fn accuracy() {
let std_instant = std::time::Instant::now();
let suspend_unaware_instant = SuspendUnawareInstant::now();
let std_elapsed = std_instant.elapsed();
let suspend_unaware_elapsed = suspend_unaware_instant.elapsed();
assert!(
std_elapsed
.as_millis()
.abs_diff(suspend_unaware_elapsed.as_millis())
< TOLERANCE_MS_U128
)
}
#[test]
fn subtraction() {
let a = SuspendUnawareInstant::now();
std::thread::sleep(Duration::from_secs(1));
let b = SuspendUnawareInstant::now();
let dur = b - a;
assert!(dur.as_millis().abs_diff(1000) < TOLERANCE_MS_U128);
}
#[test]
fn incorrect_subtraction() {
let start = SuspendUnawareInstant::now();
std::thread::sleep(Duration::from_millis(10));
let stop = SuspendUnawareInstant::now();
let res = start - stop;
assert_eq!(res, Duration::from_secs(0));
}
#[test]
fn ordering_tests() {
#[rustfmt::skip]
let cases = vec![
(create_instant(0, 0), create_instant(0, 1), Ordering::Less),
(create_instant(0, 1), create_instant(0, 2), Ordering::Less),
(create_instant(0, 100), create_instant(1, 0), Ordering::Less),
(create_instant(123, 456), create_instant(123, 456), Ordering::Equal),
(create_instant(0, 1), create_instant(0, 0), Ordering::Greater),
(create_instant(0, 2), create_instant(0, 1), Ordering::Greater),
(create_instant(1, 0), create_instant(0, 100), Ordering::Greater),
];
for (lhs, rhs, expected_result) in cases {
assert_eq!(lhs.cmp(&rhs), expected_result);
}
}
#[test]
fn addition_tests() {
#[rustfmt::skip]
let cases = vec![
(create_instant(0, 0), Duration::new(0, 1), create_instant(0, 1)),
(create_instant(0, 0), Duration::new(1, 0), create_instant(1, 0)),
(create_instant(0, 0), Duration::new(u64::MAX, NANOS_PER_SECOND - 1), create_instant(u64::MAX, NANOS_PER_SECOND - 1)),
(create_instant(1, 0), Duration::new(u64::MAX, 0), create_instant(0, 0)), (create_instant(u64::MAX, 0), Duration::new(1, 0), create_instant(0, 0)), (create_instant(u64::MAX, 1), Duration::new(0, NANOS_PER_SECOND - 1), create_instant(0, 0)), (create_instant(u64::MAX, 0), Duration::new(0, NANOS_PER_SECOND - 1), create_instant(u64::MAX, NANOS_PER_SECOND - 1)), (create_instant(0, NANOS_PER_SECOND - 1), Duration::from_secs(0) + Duration::from_nanos(10), create_instant(1, 9)), ];
for (lhs, rhs, expected_result) in cases {
assert_eq!((lhs) + (rhs), expected_result);
}
}
#[test]
fn subtraction_duration_tests() {
#[rustfmt::skip]
let cases = vec![
(create_instant(2, 0), Duration::new(1, 0), create_instant(1, 0)),
(create_instant(2, 0), Duration::new(0, 1), create_instant(1, NANOS_PER_SECOND - 1)), (create_instant(3, 10), Duration::new(1, 2), create_instant(2, 8)), (create_instant(1, 0), Duration::new(2, 0), create_instant(0, 0)), (create_instant(1, 1), Duration::new(1, 2), create_instant(0, 0)), ];
for (lhs, rhs, expected_result) in cases {
assert_eq!((lhs) - (rhs), expected_result);
}
}
#[test]
fn subtraction_instant_tests() {
#[rustfmt::skip]
let cases = [
(create_instant(10, 5), create_instant(1, 2), Duration::new(9, 3)),
(create_instant(1, 0), create_instant(2, 0), Duration::new(0, 0)), (create_instant(1, 0), create_instant(1, 1), Duration::new(0, 0)), (create_instant(2, 0), create_instant(0, 1), Duration::new(1, NANOS_PER_SECOND - 1)), ];
for (lhs, rhs, expected_result) in cases {
assert_eq!((lhs) - (rhs), expected_result);
}
}
#[tokio::test]
async fn sleep_task_test() {
let sleep_duration = Duration::from_secs(1);
let completion_deadline_duration = sleep_duration + Duration::from_millis(TOLERANCE_MS);
let task = tokio::task::spawn(crate::sleep(sleep_duration));
let res = crate::timeout(completion_deadline_duration, task).await;
assert!(res.is_ok());
}
#[tokio::test]
async fn timeout_tokio_test() {
let task_duration = Duration::from_secs(999); let suspend_unaware_deadline = Duration::from_secs(1);
let tokio_deadline = suspend_unaware_deadline + Duration::from_secs(1);
let res = crate::timeout(
tokio_deadline,
crate::timeout(
suspend_unaware_deadline,
tokio::task::spawn(async move { tokio::time::sleep(task_duration).await }),
),
)
.await;
assert!(res.is_ok());
}
#[tokio::test]
async fn timeout_table_test() {
let cases = vec![
(
Duration::from_secs(1),
Duration::from_secs(2),
Err(TimedOutError),
),
(Duration::from_secs(2), Duration::from_secs(1), Ok(())),
];
for (timeout_duration, task_duration, expected_result) in cases {
let res = crate::timeout(
timeout_duration,
tokio::task::spawn(async move { tokio::time::sleep(task_duration).await }),
)
.await;
match expected_result {
Err(expected_error) => {
assert_eq!(res.err().unwrap(), expected_error);
}
Ok(_) => {
assert!(res.unwrap().is_ok());
}
}
}
}
#[tokio::test]
async fn stress_test() {
let futures: Vec<_> = (0..50_000)
.map(|_| crate::sleep(Duration::from_millis(500)))
.collect();
let start = Instant::now();
join_all(futures).await;
assert!(start.elapsed() < Duration::from_secs(1));
}