#![allow(clippy::unwrap_used)]
use hannibal_derive::message;
use crate::{Context, DynResult, event_loop::EventLoop, prelude::Spawnable};
use super::*;
use std::future::Future;
#[derive(Debug, Default)]
pub struct MyActor(pub Option<&'static str>);
#[message]
pub struct Stop;
#[message]
pub struct Store(pub &'static str);
#[message(response=i32)]
pub struct Add(pub i32, pub i32);
impl Actor for MyActor {}
impl Handler<Stop> for MyActor {
async fn handle(&mut self, ctx: &mut Context<Self>, _: Stop) {
if let Err(error) = ctx.stop() {
eprintln!("{error}");
}
}
}
impl Handler<Store> for MyActor {
async fn handle(&mut self, _ctx: &mut Context<Self>, msg: Store) {
self.0.replace(msg.0);
}
}
impl Handler<Add> for MyActor {
async fn handle(&mut self, _: &mut Context<Self>, msg: Add) -> i32 {
msg.0 + msg.1
}
}
pub fn start<A: Actor>(actor: A) -> (impl Future<Output = DynResult<A>>, Addr<A>) {
let (event_loop, addr) = EventLoop::unbounded().create(actor);
(event_loop, addr)
}
#[test_log::test(tokio::test)]
async fn addr_call() {
let addr = MyActor::default().spawn();
let addition = addr.call(Add(1, 2)).await.unwrap();
assert_eq!(addition, 3);
}
#[test_log::test(tokio::test)]
async fn addr_send() {
let owning_addr = MyActor::default().spawn_owning();
owning_addr.send(Store("password")).await.unwrap();
let actor = owning_addr.consume().await.unwrap();
assert_eq!(actor.0, Some("password"))
}
#[test_log::test(tokio::test)]
async fn addr_send_err() {
let addr = MyActor::default().spawn();
let addr2 = addr.clone();
addr.halt().await.unwrap();
assert!(addr2.stopped());
assert!(addr2.send(Store("password")).await.is_err());
}
#[test_log::test(tokio::test)]
async fn addr_stop() {
let mut addr = MyActor::default().spawn();
let addr2 = addr.clone();
addr.stop().unwrap();
addr2.await.unwrap();
addr.await.unwrap();
}
#[test_log::test(tokio::test)]
async fn ctx_stop() {
let addr = MyActor::default().spawn();
let addr2 = addr.clone();
addr.send(Stop).await.unwrap();
addr2.await.unwrap();
addr.await.unwrap();
}
#[test_log::test(tokio::test)]
async fn addr_stopped_after_stop() {
let addr = MyActor::default().spawn();
let addr2 = addr.clone();
assert!(!addr2.stopped(), "addr2 should not be stopped");
addr.send(Stop).await.unwrap();
addr.await.unwrap();
assert!(addr2.stopped(), "addr2 should be stopped");
}
#[test_log::test(tokio::test)]
async fn weak_addr_does_not_prolong_life() {
let addr = MyActor::default().spawn();
let weak_addr = WeakAddr::from(&addr);
let weak_addr2 = addr.downgrade();
weak_addr.upgrade().unwrap();
weak_addr2.upgrade().unwrap();
addr.halt().await.unwrap();
assert!(weak_addr.upgrade().is_none());
assert!(weak_addr2.upgrade().is_none());
}
#[test_log::test(tokio::test)]
async fn weak_caller_does_not_prolong_life() {
let addr = MyActor::default().spawn();
let weak_caller = addr.weak_caller::<Stop>();
weak_caller.upgrade().unwrap();
let weak_caller2 = addr.caller::<Stop>().downgrade();
weak_caller2.upgrade().unwrap();
addr.halt().await.unwrap();
assert!(weak_caller.upgrade().is_none());
assert!(weak_caller2.upgrade().is_none());
}
#[test_log::test(tokio::test)]
async fn weak_sender_does_not_prolong_life() {
let addr = MyActor::default().spawn();
let weak_sender = addr.weak_sender::<Stop>();
weak_sender.upgrade().unwrap();
let weak_sender2 = addr.sender::<Stop>().downgrade();
weak_sender2.upgrade().unwrap();
addr.halt().await.unwrap();
assert!(weak_sender.upgrade().is_none());
assert!(weak_sender2.upgrade().is_none());
}
mod test_error_variants {
use super::*;
use crate::{error::ActorError, prelude::Spawnable};
#[test_log::test(tokio::test)]
async fn channel_closed_on_send() {
let addr = MyActor::default().spawn();
let addr2 = addr.clone();
addr.halt().await.unwrap();
assert!(addr2.send(Store("test")).await.is_err());
}
#[test_log::test(tokio::test)]
async fn channel_closed_on_call() {
let addr = MyActor::default().spawn();
let addr2 = addr.clone();
addr.halt().await.unwrap();
assert!(addr2.call(Add(1, 2)).await.is_err());
}
#[test_log::test(tokio::test)]
async fn channel_closed_on_try_send() {
let addr = MyActor::default().spawn();
let addr2 = addr.clone();
addr.halt().await.unwrap();
assert!(addr2.try_send(Store("test")).is_err());
}
#[test_log::test(tokio::test)]
async fn channel_closed_on_ping() {
let addr = MyActor::default().spawn();
let addr2 = addr.clone();
addr.halt().await.unwrap();
assert!(addr2.ping().await.is_err());
}
#[test_log::test(tokio::test)]
async fn channel_closed_via_sender() {
let addr = MyActor::default().spawn();
let sender = addr.sender::<Store>();
addr.halt().await.unwrap();
assert_eq!(
sender.send(Store("test")).await,
Err(ActorError::ChannelClosed)
);
}
#[test_log::test(tokio::test)]
async fn channel_closed_via_caller() {
let addr = MyActor::default().spawn();
let caller = addr.caller();
addr.halt().await.unwrap();
assert_eq!(caller.call(Add(1, 2)).await, Err(ActorError::ChannelClosed));
}
#[test_log::test(tokio::test)]
async fn actor_already_stopped_on_stop() {
let addr = MyActor::default().spawn();
let mut addr2 = addr.clone();
addr.halt().await.unwrap();
assert_eq!(addr2.stop(), Err(ActorError::AlreadyStopped));
}
#[test_log::test(tokio::test)]
async fn actor_already_stopped_from_context() {
let addr = MyActor::default().spawn();
let mut weak_addr = addr.downgrade();
addr.halt().await.unwrap();
assert_eq!(weak_addr.try_stop(), Err(ActorError::ActorDropped));
}
#[test_log::test(tokio::test)]
async fn actor_dropped_on_weak_sender_upgrade_and_send() {
let addr = MyActor::default().spawn();
let weak_sender = addr.weak_sender();
addr.halt().await.unwrap();
assert_eq!(
weak_sender.upgrade_and_send(Store("test")).await,
Err(ActorError::ActorDropped)
);
}
#[test_log::test(tokio::test)]
async fn actor_dropped_on_weak_caller_upgrade_and_call() {
let addr = MyActor::default().spawn();
let weak_caller = addr.weak_caller();
addr.halt().await.unwrap();
assert_eq!(
weak_caller.upgrade_and_call(Add(1, 2)).await,
Err(ActorError::ActorDropped)
);
}
#[test_log::test(tokio::test)]
async fn actor_dropped_on_weak_addr_try_stop() {
let addr = MyActor::default().spawn();
let mut weak_addr = addr.downgrade();
addr.halt().await.unwrap();
assert_eq!(weak_addr.try_stop(), Err(ActorError::ActorDropped));
}
#[test_log::test(tokio::test)]
async fn actor_dropped_on_weak_addr_try_halt() {
let addr = MyActor::default().spawn();
let mut weak_addr = addr.downgrade();
addr.halt().await.unwrap();
assert_eq!(weak_addr.try_halt().await, Err(ActorError::ActorDropped));
}
}