use std::cell::Cell;
use std::time::Duration;
use acton_reactive::prelude::*;
use acton_test::prelude::*;
const PATIENCE: Duration = Duration::from_secs(5);
#[derive(Debug, Default)]
struct NotSync {
counter: Cell<u32>,
}
#[derive(Debug, Default)]
struct AlsoNotSync {
_marker: Cell<bool>,
}
#[acton_message]
struct Bump;
#[acton_message]
struct HireWorker;
static_assertions::assert_impl_all!(NotSync: Send);
static_assertions::assert_not_impl_any!(NotSync: Sync);
static_assertions::assert_impl_all!(AlsoNotSync: Send);
static_assertions::assert_not_impl_any!(AlsoNotSync: Sync);
#[acton_test]
async fn an_actor_with_a_non_sync_model_starts_and_handles_messages() -> anyhow::Result<()> {
let mut runtime = ActonApp::launch_async().await;
let mut actor = runtime.new_actor::<NotSync>();
actor.mutate_on::<Bump>(|actor, _ctx| {
actor.model.counter.set(actor.model.counter.get() + 1);
Reply::ready()
});
actor.before_start(|_actor| async move {});
actor.after_start(|_actor| async move {});
actor.before_stop(|_actor| async move {});
actor.after_stop(|_actor| async move {});
let handle = actor.start().await;
tokio::time::timeout(PATIENCE, handle.send(Bump))
.await
.expect("a non-Sync actor takes messages");
runtime.shutdown_all().await?;
Ok(())
}
#[acton_test]
async fn a_non_sync_supervisor_can_supervise_and_restart_a_non_sync_child() -> anyhow::Result<()> {
let mut runtime = ActonApp::launch_async().await;
let (registered, mut registrations) = tokio::sync::mpsc::unbounded_channel();
let mut parent = runtime.new_actor::<NotSync>();
parent.mutate_on::<HireWorker>(move |actor, _ctx| {
let config = ActorConfig::for_supervised_child("worker", actor.handle().clone(), None)
.expect("a name plus a live parent is a valid child configuration")
.with_restart_policy(RestartPolicy::Permanent)
.with_restart_limiter(RestartLimiterConfig {
initial_backoff_ms: 10,
max_backoff_ms: 50,
backoff_multiplier: 1.0,
..RestartLimiterConfig::default()
});
let _ = registered.send(
actor.supervise_deferred(config, |child: &mut ManagedActor<Idle, AlsoNotSync>| {
child.mutate_on::<Bump>(|_actor, _ctx| Reply::ready());
}),
);
Reply::ready()
});
let parent = parent.start().await;
parent.send(HireWorker).await;
let mut child = tokio::time::timeout(PATIENCE, registrations.recv())
.await
.expect("the supervisor must answer")
.expect("the channel is open")
.expect("the first child of a name is accepted");
let first = tokio::time::timeout(PATIENCE, child.wait_running())
.await
.expect("the first start must land")?;
first.stop().await?;
let second = tokio::time::timeout(
PATIENCE,
child.wait_generation(RestartGeneration::FIRST.next()),
)
.await
.expect("a non-Sync child must be restarted like any other")?;
assert_eq!(second.id(), first.id(), "a restart keeps the child's identity");
runtime.shutdown_all().await?;
Ok(())
}