#![allow(clippy::enum_variant_names)]
use cyberex::ActorMessage;
use cyberex::xasync::call::ActorPacket;
use static_assertions::assert_impl_all;
use std::any::type_name_of_val;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Notify;
#[derive(ActorMessage)]
enum GetTypeNameCommand {
GetStringTypeName(ActorPacket<String, String>),
GetI32TypeName(ActorPacket<i32, String>),
Timeout(ActorPacket<(), ()>),
ThisError(ActorPacket<(), ()>),
Event(String),
}
#[derive(thiserror::Error, Debug, PartialEq)]
#[error("Custom error: {message}")]
struct CustomError {
message: String,
}
#[derive(ActorMessage)]
struct GetVoid(ActorPacket<(), ()>);
#[derive(ActorMessage)]
struct Event(String);
#[tokio::test]
async fn test_case() {
let (caller, mut replyer) = GetTypeNameCommand::actor();
assert_impl_all!(GetTypeNameCommandCaller: std::fmt::Debug, Clone);
assert_impl_all!(GetTypeNameCommandWeakCaller: std::fmt::Debug, Clone);
assert_eq!(type_name_of_val(&caller), "drive_actor_tests::GetTypeNameCommandCaller");
assert_eq!(
type_name_of_val(&caller.downgrade()),
"drive_actor_tests::GetTypeNameCommandWeakCaller"
);
assert_eq!(
type_name_of_val(&replyer),
"drive_actor_tests::GetTypeNameCommandReplyer"
);
let event_notified = Arc::new(Notify::new());
tokio::spawn({
let event_notified = event_notified.clone();
async move {
while let Some(command) = replyer.recv().await {
match command {
GetTypeNameCommand::GetI32TypeName(packet) => {
let _ = packet.return_(Ok("i32".to_string()));
},
GetTypeNameCommand::GetStringTypeName(packet) => {
let _ = packet.return_ok("string".to_string());
},
GetTypeNameCommand::Timeout(packet) => {
tokio::spawn(async move {
tokio::time::sleep(Duration::from_secs(10)).await;
let _ = packet.return_ok(());
});
},
GetTypeNameCommand::ThisError(packet) => {
let custom_err = CustomError {
message: "test error".to_string(),
};
let _ = packet.return_fail(anyhow::anyhow!(custom_err));
},
GetTypeNameCommand::Event(event_str) => {
assert_eq!(event_str, "Event_str");
event_notified.notify_one();
},
};
}
}
});
{
let result_str = caller.get_string_type_name("hello".to_string()).await.unwrap();
assert_eq!(result_str, "string");
}
{
let weak_caller = caller.downgrade();
let upgraded_caller = weak_caller.upgrade().unwrap();
let result_str = upgraded_caller.get_i32_type_name(1).await.unwrap();
assert_eq!(result_str, "i32");
}
{
let result_str = caller.get_i32_type_name(1).await.unwrap();
assert_eq!(result_str, "i32");
}
{
let result_str = caller.timeout_timeout((), Duration::from_secs(1)).await.unwrap_err();
assert_eq!(
result_str.to_string(),
r#"Fail to `timeout`, error: Receive result fail, timeout: 1s"#
);
}
{
let result = caller.this_error(()).await;
assert!(result.is_err());
let err = result.unwrap_err();
assert_eq!(
format!("{}", err),
"Fail to `this_error`, error: Custom error: test error"
);
let custom_error = err.downcast_ref::<CustomError>();
assert!(custom_error.is_some());
assert_eq!(custom_error.unwrap().message, "test error");
}
{
caller.event("Event_str".to_string());
event_notified.notified().await;
}
}
#[tokio::test]
async fn test_single_struct_case() {
let (caller, mut replyer) = GetVoid::actor();
assert_impl_all!(GetVoidCaller: std::fmt::Debug, Clone);
assert_impl_all!(GetVoidWeakCaller: std::fmt::Debug, Clone);
assert_eq!(type_name_of_val(&caller), "drive_actor_tests::GetVoidCaller");
assert_eq!(
type_name_of_val(&caller.downgrade()),
"drive_actor_tests::GetVoidWeakCaller"
);
assert_eq!(type_name_of_val(&replyer), "drive_actor_tests::GetVoidReplyer");
tokio::spawn(async move {
while let Some(command) = replyer.recv().await {
let _ = command.0.return_ok(());
}
});
{
let weak_caller = caller.downgrade();
let upgraded_caller = weak_caller.upgrade().unwrap();
upgraded_caller.get_void(()).await.unwrap();
}
caller.get_void(()).await.unwrap();
}
#[tokio::test]
async fn test_single_struct_weak_upgrade_none_after_drop() {
let weak_caller = {
let (void_caller, _void_replyer) = GetVoid::actor();
void_caller.downgrade()
};
assert!(weak_caller.upgrade().is_none());
}
#[tokio::test]
async fn test_event_struct_case() {
let (caller, mut replyer) = Event::actor();
assert_impl_all!(EventCaller: std::fmt::Debug, Clone);
assert_impl_all!(EventWeakCaller: std::fmt::Debug, Clone);
assert_eq!(type_name_of_val(&caller), "drive_actor_tests::EventCaller");
assert_eq!(
type_name_of_val(&caller.downgrade()),
"drive_actor_tests::EventWeakCaller"
);
assert_eq!(type_name_of_val(&replyer), "drive_actor_tests::EventReplyer");
let event_notified = Arc::new(Notify::new());
tokio::spawn({
let event_notified = event_notified.clone();
async move {
while let Some(command) = replyer.recv().await {
assert_eq!(command.0, "Event_struct");
event_notified.notify_one();
}
}
});
{
let weak_caller = caller.downgrade();
let upgraded_caller = weak_caller.upgrade().unwrap();
upgraded_caller.event("Event_struct".to_string());
event_notified.notified().await;
}
caller.event("Event_struct".to_string());
event_notified.notified().await;
}
#[tokio::test]
async fn test_event_struct_weak_upgrade_none_after_drop() {
let weak_caller = {
let (event_caller, _event_replyer) = Event::actor();
event_caller.downgrade()
};
assert!(weak_caller.upgrade().is_none());
}
#[tokio::test]
async fn test_caller_closed_after_replyer_dropped() {
let (caller, replyer) = GetVoid::actor();
drop(replyer);
caller.as_ref().closed().await;
}