use assert2::{assert, let_assert};
use futures::future;
use std::future::Future;
use std::time::Duration;
use async_shutdown::ShutdownManager;
#[track_caller]
fn test_timeout(test: impl Future<Output = ()>) {
let_assert!(Ok(runtime) = tokio::runtime::Runtime::new(), "failed to initialize tokio runtime");
runtime.block_on(async move {
let test = tokio::time::timeout(Duration::from_millis(100), test);
assert!(let Ok(()) = test.await, "test timed out");
});
}
#[test]
fn shutdown() {
test_timeout(async {
let shutdown = ShutdownManager::new();
assert!(let Ok(()) = shutdown.trigger_shutdown(1));
assert!(shutdown.wait_shutdown_triggered().await == 1);
assert!(shutdown.wait_shutdown_complete().await == 1);
});
test_timeout(async {
let shutdown = ShutdownManager::new();
tokio::spawn({
let shutdown = shutdown.clone();
async move {
tokio::time::sleep(Duration::from_millis(20)).await;
assert!(let Ok(()) = shutdown.trigger_shutdown(2));
}
});
assert!(shutdown.wait_shutdown_triggered().await == 2);
assert!(shutdown.wait_shutdown_complete().await == 2);
});
}
#[test]
fn shutdown_only_works_once() {
let shutdown = ShutdownManager::new();
assert!(let Ok(()) = shutdown.trigger_shutdown("first"));
assert!(let Err(async_shutdown::ShutdownAlreadyStarted {
shutdown_reason: "first",
ignored_reason: "second",
..
}) = shutdown.trigger_shutdown("second"));
}
#[test]
fn wrap_cancel() {
test_timeout(async {
let shutdown = ShutdownManager::new();
let task = tokio::spawn(shutdown.wrap_cancel(future::pending::<()>()));
assert!(let Ok(()) = shutdown.trigger_shutdown("goodbye!"));
let_assert!(Ok(Err(reason)) = task.await);
assert!(reason == "goodbye!");
});
test_timeout(async {
let shutdown = ShutdownManager::new();
let wait_shutdown_triggered = shutdown.wait_shutdown_triggered();
let task = tokio::spawn(wait_shutdown_triggered.wrap_cancel(future::pending::<()>()));
assert!(let Ok(()) = shutdown.trigger_shutdown("wooh"));
let_assert!(Ok(Err(reason)) = task.await);
assert!(reason == "wooh");
});
}
#[test]
fn wrap_cancel_no_shutdown() {
test_timeout(async {
let shutdown = ShutdownManager::<()>::new();
let task = tokio::spawn(shutdown.wrap_cancel(future::ready(10)));
assert!(let Ok(Ok(10)) = task.await);
});
test_timeout(async {
let shutdown = ShutdownManager::<()>::new();
let task = tokio::spawn(shutdown.wrap_cancel(async {
tokio::time::sleep(Duration::from_millis(20)).await;
10u32
}));
assert!(let Ok(Ok(10)) = task.await);
});
}
#[test]
fn delay_token() {
test_timeout(async {
let shutdown = ShutdownManager::new();
let_assert!(Ok(delay) = shutdown.delay_shutdown_token());
assert!(let Ok(()) = shutdown.trigger_shutdown(10));
shutdown.wait_shutdown_triggered().await;
assert!(shutdown.is_shutdown_completed() == false);
tokio::spawn(async move {
tokio::time::sleep(Duration::from_millis(20)).await;
drop(delay);
});
shutdown.wait_shutdown_complete().await;
});
}
#[test]
fn wrap_delay() {
test_timeout(async {
let shutdown = ShutdownManager::new();
let_assert!(Ok(delay) = shutdown.delay_shutdown_token());
assert!(let Ok(()) = shutdown.trigger_shutdown(10));
tokio::spawn(delay.wrap_future(async move {
tokio::time::sleep(Duration::from_millis(10)).await;
}));
shutdown.wait_shutdown_triggered().await;
shutdown.wait_shutdown_complete().await;
});
}
#[test]
fn wait_for_shutdown_complete_in_task_without_delay_tokens() {
test_timeout(async {
let shutdown = ShutdownManager::new();
tokio::spawn({
let shutdown = shutdown.clone();
async move {
tokio::time::sleep(Duration::from_millis(10)).await;
assert!(let Ok(()) = shutdown.trigger_shutdown(10));
}
});
let task = tokio::spawn({
let shutdown = shutdown.clone();
async move {
assert!(shutdown.wait_shutdown_complete().await == 10);
}
});
assert!(let Ok(()) = task.await);
});
}
#[test]
fn delay_token_too_late() {
let shutdown = ShutdownManager::new();
assert!(let Ok(()) = shutdown.trigger_shutdown(()));
assert!(let Err(async_shutdown::ShutdownAlreadyCompleted { .. }) = shutdown.delay_shutdown_token());
assert!(let Err(async_shutdown::ShutdownAlreadyCompleted { .. }) = shutdown.wrap_delay_shutdown(future::pending::<()>()));
}
#[test]
fn vital_token() {
test_timeout(async {
let shutdown = ShutdownManager::new();
let trigger_shutdown_token = shutdown.trigger_shutdown_token("stop!");
drop(trigger_shutdown_token);
assert!(shutdown.wait_shutdown_triggered().await == "stop!");
assert!(shutdown.wait_shutdown_complete().await == "stop!");
});
test_timeout(async {
let shutdown = ShutdownManager::new();
let vital = shutdown.trigger_shutdown_token("done");
tokio::spawn(async move {
tokio::time::sleep(Duration::from_millis(20)).await;
drop(vital);
});
assert!(shutdown.wait_shutdown_triggered().await == "done");
assert!(shutdown.wait_shutdown_complete().await == "done");
});
}
#[test]
fn wrap_vital() {
test_timeout(async {
let shutdown = ShutdownManager::new();
tokio::spawn(shutdown.wrap_trigger_shutdown("sleep done", async move {
tokio::time::sleep(Duration::from_millis(20)).await;
}));
assert!(shutdown.wait_shutdown_triggered().await == "sleep done");
assert!(shutdown.wait_shutdown_complete().await == "sleep done");
});
test_timeout(async {
let shutdown = ShutdownManager::new();
tokio::spawn(shutdown.wrap_trigger_shutdown("stop", future::ready(())));
assert!(shutdown.wait_shutdown_triggered().await == "stop");
assert!(shutdown.wait_shutdown_complete().await == "stop");
});
}