#[cfg(test)]
#[cfg(feature = "async-std")]
mod async_std_tests {
use std::{io, sync::Arc};
use actor_helper::{ActorState, Handle};
use actor_helper::{act, act_ok};
struct TestActor {
value: i32,
}
struct TestApi {
handle: Handle<TestActor, io::Error>,
}
impl TestApi {
fn new() -> Self {
Self {
handle: Handle::spawn(TestActor { value: 0 }).0,
}
}
async fn stop_actor(&self) {
self.handle.shutdown();
self.handle.wait_stopped().await;
}
async fn set_value(&self, value: i32) -> io::Result<()> {
self.handle
.call(act_ok!(actor => async move {
actor.value = value;
}))
.await
}
async fn get_value(&self) -> io::Result<i32> {
self.handle
.call(act_ok!(actor => async move {
actor.value
}))
.await
}
async fn increment(&self, by: i32) -> io::Result<()> {
self.handle
.call(act_ok!(actor => async move {
actor.value += by;
}))
.await
}
async fn set_positive(&self, value: i32) -> io::Result<()> {
self.handle
.call(act!(actor => async move {
if value <= 0 {
Err(io::Error::other("Value must be positive"))
} else {
actor.value = value;
Ok(())
}
}))
.await
}
async fn multiply(&self, factor: i32) -> io::Result<i32> {
self.handle
.call(act_ok!(actor => async move {
actor.value *= factor;
actor.value
}))
.await
}
async fn is_running(&self) -> bool {
self.handle.state() == ActorState::Running
}
}
#[async_std::test]
async fn test_basic_operations() {
let api = TestApi::new();
assert_eq!(api.get_value().await.unwrap(), 0);
api.set_value(42).await.unwrap();
assert_eq!(api.get_value().await.unwrap(), 42);
api.increment(8).await.unwrap();
assert_eq!(api.get_value().await.unwrap(), 50);
}
#[async_std::test]
async fn test_error_handling() {
let api = TestApi::new();
let result = api.set_positive(-5).await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("positive"));
api.set_positive(10).await.unwrap();
assert_eq!(api.get_value().await.unwrap(), 10);
}
#[async_std::test]
async fn test_return_values() {
let api = TestApi::new();
api.set_value(7).await.unwrap();
let result = api.multiply(3).await.unwrap();
assert_eq!(result, 21);
assert_eq!(api.get_value().await.unwrap(), 21);
}
#[async_std::test]
async fn test_concurrent_access() {
let api = Arc::new(TestApi::new());
let mut handles = vec![];
for i in 0..10 {
let api_clone = api.clone();
api_clone.increment(i).await.unwrap();
handles.push(());
}
let final_value = api.get_value().await.unwrap();
assert_eq!(final_value, 45);
}
#[async_std::test]
async fn test_sequential_operations() {
let api = TestApi::new();
api.set_value(1).await.unwrap();
for _ in 0..5 {
let current = api.get_value().await.unwrap();
api.set_value(current * 2).await.unwrap();
}
assert_eq!(api.get_value().await.unwrap(), 32);
}
#[async_std::test]
async fn test_clone_handle() {
let api1 = TestApi::new();
let api2 = TestApi {
handle: api1.handle.clone(),
};
api1.set_value(100).await.unwrap();
assert_eq!(api2.get_value().await.unwrap(), 100);
api2.increment(50).await.unwrap();
assert_eq!(api1.get_value().await.unwrap(), 150);
}
#[async_std::test]
async fn test_actor_lifecycle() {
let api = TestApi::new();
assert!(api.is_running().await);
api.stop_actor().await;
assert!(!api.is_running().await);
assert!(api.get_value().await.is_err());
}
struct CounterActor {
count: i32,
}
#[async_std::test]
async fn test_shared_state() {
let (handle, _) = Handle::<CounterActor, io::Error>::spawn(CounterActor { count: 0 });
handle
.call(act_ok!(actor => async move {
actor.count += 1;
}))
.await
.unwrap();
assert_eq!(
handle
.call(act_ok!(actor => async move { actor.count }))
.await
.unwrap(),
1
);
}
#[async_std::test]
async fn test_async_action() {
let api = TestApi::new();
api.handle
.call(act!(actor => async move {
std::thread::sleep(std::time::Duration::from_millis(10));
actor.value = 999;
Ok(())
}))
.await
.unwrap();
assert_eq!(api.get_value().await.unwrap(), 999);
}
#[async_std::test]
async fn test_multiple_handles_same_actor() {
let (handle1, _) = Handle::<TestActor, io::Error>::spawn(TestActor { value: 0 });
let handle2 = handle1.clone();
let handle3 = handle1.clone();
handle1
.call(act_ok!(actor => async move { actor.value += 10; }))
.await
.unwrap();
handle2
.call(act_ok!(actor => async move { actor.value *= 2; }))
.await
.unwrap();
let result = handle3
.call(act_ok!(actor => async move { actor.value }))
.await
.unwrap();
assert_eq!(result, 20);
}
struct PanicOnDisconnectActor;
#[async_std::test]
async fn test_actor_loop_panic_is_returned_as_error() {
let prev = std::panic::take_hook();
std::panic::set_hook(Box::new(|_| {}));
let join_handle = {
let (handle, join_handle) = Handle::<PanicOnDisconnectActor, io::Error>::spawn_with(
PanicOnDisconnectActor,
|_actor, _rx| async { panic!("blocking actor panic") },
);
drop(handle);
join_handle
};
let result = join_handle.await;
let error = result.unwrap_err().to_string();
std::panic::set_hook(prev);
assert!(error.contains("panic in actor loop"));
assert!(error.contains("blocking actor panic"));
}
}