swactor/tests/runtime_tests.rs
Zachery Aaron Shores-Chmielewski c62b20c732 feat(WIP): refactor router execution
In single threaded contexts, the Router now is treated as yet another actor
on the queue. In multithreaded contexts, it gets its own dedicated thread.
2026-01-25 12:54:01 +07:00

137 lines
3.4 KiB
Rust

use std::thread;
use std::time::Duration;
use swactor::{
actor::{ActorAddress, ActorInterface},
runtime::{Inbox, Runtime, RuntimeFlavor},
};
// ============================================================================
// Test Helpers
// ============================================================================
#[derive(Clone)]
struct PingMessage {
reply_to: ActorAddress,
}
#[derive(Clone)]
struct PongMessage;
struct PongActor;
impl ActorInterface for PongActor {
type Incoming = PingMessage;
type Response = PongMessage;
fn handle(&mut self, ctx: &Runtime, msg: PingMessage) {
let _ = ctx.send_to(msg.reply_to, PongMessage);
}
}
/// An actor that forwards messages to another address
struct ForwarderActor {
target: ActorAddress,
}
#[derive(Clone)]
struct ForwardMessage(usize);
impl ActorInterface for ForwarderActor {
type Incoming = ForwardMessage;
type Response = ();
fn handle(&mut self, ctx: &Runtime, msg: ForwardMessage) {
let _ = ctx.send_to(self.target, msg);
}
}
#[test]
fn test_single_threaded_ping_pong() {
let mut rt = Runtime::new();
let inbox: Inbox<PongMessage> = rt.new_inbox();
let pong_addr = rt.spawn(PongActor).expect("spawn pong");
// Send ping
rt.send_to(
pong_addr,
PingMessage {
reply_to: *inbox.addr(),
},
)
.unwrap();
// Tick until we get a response
for _ in 0..10 {
rt.tick();
if inbox.try_recv().is_some() {
return; // Success!
}
}
panic!("Did not receive pong response");
}
#[test]
fn test_single_threaded_message_chain() {
let mut rt = Runtime::new();
let inbox: Inbox<ForwardMessage> = rt.new_inbox();
// Create a chain: A -> B -> C -> inbox
let c_addr = rt
.spawn(ForwarderActor {
target: *inbox.addr(),
})
.unwrap();
let b_addr = rt.spawn(ForwarderActor { target: c_addr }).unwrap();
let a_addr = rt.spawn(ForwarderActor { target: b_addr }).unwrap();
// Send message to start of chain
rt.send_to(a_addr, ForwardMessage(42)).unwrap();
// Tick until message arrives
for _ in 0..20 {
rt.tick();
if let Some(ForwardMessage(val)) = inbox.try_recv() {
assert_eq!(val, 42);
return;
}
}
panic!("Message did not traverse the chain");
}
// #[test]
// fn test_multithreaded_message_passing() {
// let rt = Runtime::new(1000, RuntimeFlavor::Multithreaded { workers: 4 });
// let inbox: Inbox<ForwardMessage> = rt.new_inbox();
// // Create a longer chain to exercise multi-threading
// let mut target = *inbox.addr();
// for _ in 0..20 {
// target = rt.spawn(ForwarderActor { target }).unwrap();
// }
// let start_addr = target;
// // Send message
// rt.send_to(start_addr, ForwardMessage(999)).unwrap();
// // Spawn thread to check for result and shutdown
// let ctx = rt.run();
// let inbox_check = thread::spawn(move || {
// for _ in 0..100 {
// thread::sleep(Duration::from_millis(10));
// if let Some(ForwardMessage(val)) = inbox.try_recv() {
// ctx.shutdown();
// return Some(val);
// }
// }
// ctx.shutdown();
// None
// });
// let result = inbox_check.join().unwrap();
// assert_eq!(result, Some(999));
// }