#![allow(dead_code, unused_doc_comments)]
use std::time::Duration;
use tracing::{info, trace};
use acton_reactive::prelude::*;
use acton_test::prelude::*;
use crate::setup::{
actors::{comedian::Comedian, counter::Counter, messenger::Messenger, pool_item::PoolItem},
initialize_tracing,
messages::{AudienceReactionMsg, FunnyJoke, FunnyJokeFor, Ping, Tally},
};
mod setup;
#[acton_test]
async fn test_async_reactor() -> anyhow::Result<()> {
initialize_tracing();
let mut runtime: ActorRuntime = ActonApp::launch_async().await;
let actor_config = ActorConfig::new(Ern::with_root("improve_show").unwrap(), None, None)?;
let mut comedian_actor_builder = runtime.new_actor_with_config::<Comedian>(actor_config);
comedian_actor_builder
.mutate_on::<FunnyJoke>(|actor, _envelope| {
actor.model.jokes_told += 1;
let actor_handle = actor.handle().clone();
Reply::pending(async move {
trace!("emitting async");
actor_handle.send(Ping).await;
})
})
.mutate_on::<AudienceReactionMsg>(|actor, envelope| {
trace!("Received Audience Reaction");
match envelope.message() {
AudienceReactionMsg::Chuckle => actor.model.funny += 1,
AudienceReactionMsg::Groan => actor.model.bombers += 1,
}
Reply::ready()
})
.mutate_on::<Ping>(|_actor, _envelope| {
trace!("PING");
Reply::ready()
})
.after_stop(|actor| {
info!(
"Jokes told at {}: {}\tFunny: {}\tBombers: {}",
actor.id(), actor.model.jokes_told, actor.model.funny,
actor.model.bombers
);
assert_eq!(actor.model.jokes_told, 2);
Reply::ready()
});
let comedian_handle = comedian_actor_builder.start().await;
comedian_handle.send(FunnyJoke::ChickenCrossesRoad).await;
comedian_handle.send(FunnyJoke::Pun).await;
tokio::time::sleep(Duration::from_secs(1)).await;
runtime.shutdown_all().await?;
Ok(())
}
#[acton_test]
async fn test_lifecycle_handlers() -> anyhow::Result<()> {
initialize_tracing();
let mut runtime: ActorRuntime = ActonApp::launch_async().await;
let mut counter_actor_builder = runtime.new_actor::<Counter>();
counter_actor_builder
.mutate_on::<Tally>(|actor, _envelope| {
info!("on tally");
actor.model.count += 1;
Reply::ready()
})
.after_stop(|actor| {
assert_eq!(4, actor.model.count);
trace!("on stopping");
Reply::ready()
});
let counter_handle = counter_actor_builder.start().await;
for _ in 0..4 {
counter_handle.send(Tally {}).await;
}
let mut messenger_actor_builder = runtime.new_actor::<Messenger>();
messenger_actor_builder
.after_start(|_actor| {
trace!("*");
Reply::ready()
})
.after_stop(|_actor| {
trace!("*");
Reply::ready()
});
let _messenger_handle = messenger_actor_builder.start().await;
tokio::time::sleep(Duration::from_millis(100)).await;
runtime.shutdown_all().await?;
Ok(())
}
#[acton_test]
async fn test_child_actor() -> anyhow::Result<()> {
initialize_tracing();
let mut runtime: ActorRuntime = ActonApp::launch_async().await;
let parent_config = ActorConfig::new(
Ern::with_root("test_child_actor_parent").unwrap(),
None,
None,
)?;
let parent_actor_builder = runtime.new_actor_with_config::<PoolItem>(parent_config);
let child_config = ActorConfig::new(
Ern::with_root("test_child_actor_chile").unwrap(),
None,
None,
)?;
let mut child_actor_builder = runtime.new_actor_with_config::<PoolItem>(child_config);
child_actor_builder
.mutate_on::<Ping>(|actor, envelope| {
match envelope.message() {
Ping => {
actor.model.receive_count += 1;
}
}
Reply::ready()
})
.after_stop(|actor| {
info!("Child processed {} PINGs", actor.model.receive_count);
assert_eq!(
actor.model.receive_count, 22,
"Child actor did not process the expected number of PINGs"
);
Reply::ready()
});
let child_id = child_actor_builder.id().clone();
let parent_handle = parent_actor_builder.start().await;
parent_handle.supervise(child_actor_builder).await?;
assert_eq!(
parent_handle.children().len(),
1,
"Parent handle missing its child after supervision"
);
info!(child = &child_id.to_string(), "Searching all children for");
let found_child_handle = parent_handle.find_child(&child_id);
assert!(
found_child_handle.is_some(),
"Couldn't find child with id {child_id}"
);
let child_handle = found_child_handle.unwrap();
for _ in 0..22 {
trace!("Emitting PING");
child_handle.send(Ping).await;
}
trace!("Stopping parent actor (which should stop the child)");
tokio::time::sleep(Duration::from_millis(100)).await;
runtime.shutdown_all().await?;
Ok(())
}
#[acton_test]
async fn test_find_child_actor() -> anyhow::Result<()> {
initialize_tracing();
let mut runtime: ActorRuntime = ActonApp::launch_async().await;
let parent_actor_builder = runtime.new_actor::<PoolItem>();
let parent_handle = parent_actor_builder.start().await;
let child_config = ActorConfig::new(
Ern::with_root("test_find_child_actor_child").unwrap(),
None,
None,
)?;
let child_actor_builder = runtime.new_actor_with_config::<PoolItem>(child_config);
let child_id = child_actor_builder.id().clone();
parent_handle.supervise(child_actor_builder).await?;
assert_eq!(
parent_handle.children().len(),
1,
"Parent handle missing its child after supervision"
);
info!(child = &child_id.to_string(), "Searching all children for");
let found_child_handle = parent_handle.find_child(&child_id);
assert!(
found_child_handle.is_some(),
"Couldn't find child with id {child_id}"
);
let _child_handle = found_child_handle.unwrap(); tokio::time::sleep(Duration::from_millis(100)).await;
runtime.shutdown_all().await?;
Ok(())
}
#[acton_test]
async fn test_actor_mutation() -> anyhow::Result<()> {
initialize_tracing();
let mut runtime: ActorRuntime = ActonApp::launch_async().await;
let actor_config =
ActorConfig::new(Ern::with_root("test_actor_mutation").unwrap(), None, None)?;
let mut comedian_actor_builder = runtime.new_actor_with_config::<Comedian>(actor_config);
comedian_actor_builder
.mutate_on::<FunnyJoke>(|actor, envelope| {
actor.model.jokes_told += 1;
let self_envelope = actor.new_envelope();
let message = envelope.message().clone();
Reply::pending(async move {
if let Some(self_envelope) = self_envelope {
match message {
FunnyJoke::ChickenCrossesRoad => {
let () = self_envelope.send(AudienceReactionMsg::Chuckle).await;
}
FunnyJoke::Pun => {
let () = self_envelope.send(AudienceReactionMsg::Groan).await;
}
}
}
})
})
.mutate_on::<AudienceReactionMsg>(|actor, envelope| {
match envelope.message() {
AudienceReactionMsg::Chuckle => actor.model.funny += 1,
AudienceReactionMsg::Groan => actor.model.bombers += 1,
}
Reply::ready()
})
.after_stop(|actor| {
info!(
"Jokes told at {}: {}\tFunny: {}\tBombers: {}",
actor.id(),
actor.model.jokes_told,
actor.model.funny,
actor.model.bombers
);
assert_eq!(actor.model.jokes_told, 2);
assert_eq!(actor.model.funny, 1);
assert_eq!(actor.model.bombers, 1);
Reply::ready()
});
let comedian_handle = comedian_actor_builder.start().await;
comedian_handle.send(FunnyJoke::ChickenCrossesRoad).await;
comedian_handle.send(FunnyJoke::Pun).await;
tokio::time::sleep(Duration::from_millis(100)).await;
runtime.shutdown_all().await?;
Ok(())
}
#[acton_test]
async fn test_child_count_in_reactor() -> anyhow::Result<()> {
initialize_tracing();
let mut runtime: ActorRuntime = ActonApp::launch_async().await;
let mut parent_actor_builder = runtime.new_actor::<Comedian>();
parent_actor_builder.mutate_on::<FunnyJokeFor>(|actor, envelope| {
if let FunnyJokeFor::ChickenCrossesRoad(child_id) = envelope.message().clone() {
info!("Got a funny joke for {}", &child_id);
assert_eq!(
actor.handle().children().len(),
1,
"Parent actor missing any children in handler"
);
trace!("Parent actor has children: {:?}", actor.handle().children());
assert!(
actor.handle().find_child(&child_id).is_some(),
"No child found with ID {} in handler",
&child_id
);
let maybe_child = actor.handle().find_child(&child_id);
Reply::pending(async move {
if let Some(child_handle) = maybe_child {
trace!("Pinging child {}", &child_id);
child_handle.send(Ping).await;
} else {
tracing::error!("No child found with ID {}", &child_id);
}
})
} else {
Reply::ready()
}
});
let child_config = ActorConfig::new(Ern::with_root("child").unwrap(), None, None)?;
let mut child_actor_builder = runtime.new_actor_with_config::<Counter>(child_config);
info!(
"Created child actor builder with id: {}",
child_actor_builder.id()
);
child_actor_builder.mutate_on::<Ping>(|actor, _envelope| {
info!("Child {} received Ping from parent actor", actor.id());
Reply::ready()
});
let child_id = child_actor_builder.id().clone();
parent_actor_builder
.handle()
.supervise(child_actor_builder)
.await?;
assert_eq!(
parent_actor_builder.handle().children().len(),
1,
"Parent builder missing its child after supervision"
);
let parent_handle = parent_actor_builder.start().await;
assert_eq!(
parent_handle.children().len(),
1,
"Started parent handle missing its child"
);
parent_handle
.send(FunnyJokeFor::ChickenCrossesRoad(child_id))
.await;
tokio::time::sleep(Duration::from_millis(100)).await;
runtime.shutdown_all().await?;
Ok(())
}