use std::sync::{
Arc,
atomic::{AtomicUsize, Ordering},
};
use cordis_rs::{App, Error, Plugin, PluginContext, Resource, Result};
struct CooperativeTaskPlugin;
impl Plugin for CooperativeTaskPlugin {
type Config = Arc<AtomicUsize>;
fn name(&self) -> &'static str {
"cooperative-task"
}
async fn apply(&self, ctx: PluginContext, exits: Arc<Self::Config>) -> Result<()> {
ctx.spawn(move |token| async move {
token.cancelled().await;
exits.fetch_add(1, Ordering::SeqCst);
Ok(())
})?;
Ok(())
}
}
#[tokio::test]
async fn scope_dispose_cancels_and_waits_for_cooperative_task() -> Result<()> {
let app = App::new();
let exits = Arc::new(AtomicUsize::new(0));
let scope = app.install(CooperativeTaskPlugin, exits.clone()).await?;
scope.dispose().await?;
assert_eq!(exits.load(Ordering::SeqCst), 1);
Ok(())
}
struct FailingTaskPlugin;
impl Plugin for FailingTaskPlugin {
type Config = ();
fn name(&self) -> &'static str {
"failing-task"
}
async fn apply(&self, ctx: PluginContext, _: Arc<Self::Config>) -> Result<()> {
ctx.spawn(|token| async move {
token.cancelled().await;
Err(Error::cleanup("task failed"))
})?;
Ok(())
}
}
#[tokio::test]
async fn task_error_is_returned_by_scope_dispose() -> Result<()> {
let app = App::new();
let scope = app.install(FailingTaskPlugin, ()).await?;
let error = scope.dispose().await.unwrap_err();
assert!(matches!(error, Error::Cleanup(message) if message == "task failed"));
Ok(())
}
struct ManagedResource {
cancelled: Arc<AtomicUsize>,
disposed: Arc<AtomicUsize>,
}
impl Resource for ManagedResource {
fn cancel(&self) {
self.cancelled.fetch_add(1, Ordering::SeqCst);
}
async fn dispose(self: Box<Self>) -> Result<()> {
self.disposed.fetch_add(1, Ordering::SeqCst);
Ok(())
}
}
struct ResourcePlugin;
impl Plugin for ResourcePlugin {
type Config = (Arc<AtomicUsize>, Arc<AtomicUsize>);
fn name(&self) -> &'static str {
"managed-resource"
}
async fn apply(&self, ctx: PluginContext, config: Arc<Self::Config>) -> Result<()> {
ctx.manage(ManagedResource {
cancelled: config.0.clone(),
disposed: config.1.clone(),
})
}
}
#[tokio::test]
async fn managed_resource_is_cancelled_then_disposed_once() -> Result<()> {
let app = App::new();
let cancelled = Arc::new(AtomicUsize::new(0));
let disposed = Arc::new(AtomicUsize::new(0));
let scope = app
.install(ResourcePlugin, (cancelled.clone(), disposed.clone()))
.await?;
scope.dispose().await?;
app.shutdown().await?;
assert_eq!(cancelled.load(Ordering::SeqCst), 1);
assert_eq!(disposed.load(Ordering::SeqCst), 1);
Ok(())
}