test: Cycle 16 — spawn+send same tick, 21-actor mixed runtime, alternating alloc
Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
This commit is contained in:
parent
4a0cde4b41
commit
fd0181291c
1 changed files with 139 additions and 0 deletions
|
|
@ -2875,3 +2875,142 @@ proptest! {
|
||||||
prop_assert_eq!(received.0, payload);
|
prop_assert_eq!(received.0, payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Spawn and send in same tick: message delivered on first tick ─────────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wasm_actor_receives_message_sent_in_spawn_tick() {
|
||||||
|
let engine = SharedEngine::new().unwrap();
|
||||||
|
let actor = WasmActorBuilder::new(engine, guest_wasm("echo"))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let rt = Runtime::new(RuntimeConfig::default());
|
||||||
|
let inbox = rt.new_inbox::<ByteMessage>().unwrap();
|
||||||
|
|
||||||
|
let addr = rt.spawn(actor).unwrap();
|
||||||
|
rt.send_to(addr, framed_msg(inbox.addr(), b"first-tick")).unwrap();
|
||||||
|
|
||||||
|
// Single tick should spawn the actor AND deliver the message
|
||||||
|
rt.tick();
|
||||||
|
|
||||||
|
let received = inbox.try_recv().expect("message sent before first tick should be processed");
|
||||||
|
assert_eq!(received.0, b"first-tick");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── WASM actor coexists with many native actors ─────────────────────────────
|
||||||
|
|
||||||
|
struct Counter {
|
||||||
|
count: std::sync::Arc<std::sync::atomic::AtomicUsize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Ping;
|
||||||
|
|
||||||
|
impl ActorInterface for Counter {
|
||||||
|
type Incoming = Ping;
|
||||||
|
type Response = ();
|
||||||
|
fn handle(&mut self, _ctx: &Ctx, _msg: Ping) {
|
||||||
|
self.count.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wasm_actor_works_alongside_many_native_actors() {
|
||||||
|
let engine = SharedEngine::new().unwrap();
|
||||||
|
let echo = WasmActorBuilder::new(engine, guest_wasm("echo"))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let rt = Runtime::new(RuntimeConfig::default());
|
||||||
|
let inbox = rt.new_inbox::<ByteMessage>().unwrap();
|
||||||
|
|
||||||
|
let counts: Vec<_> = (0..20)
|
||||||
|
.map(|_| std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut native_addrs = Vec::new();
|
||||||
|
for count in &counts {
|
||||||
|
let addr = rt.spawn(Counter { count: count.clone() }).unwrap();
|
||||||
|
native_addrs.push(addr);
|
||||||
|
}
|
||||||
|
let echo_addr = rt.spawn(echo).unwrap();
|
||||||
|
|
||||||
|
// Send to all actors in same tick
|
||||||
|
for addr in &native_addrs {
|
||||||
|
rt.send_to(*addr, Ping).unwrap();
|
||||||
|
}
|
||||||
|
rt.send_to(echo_addr, framed_msg(inbox.addr(), b"mixed")).unwrap();
|
||||||
|
rt.tick();
|
||||||
|
|
||||||
|
for (i, count) in counts.iter().enumerate() {
|
||||||
|
assert_eq!(
|
||||||
|
count.load(std::sync::atomic::Ordering::SeqCst), 1,
|
||||||
|
"native actor {i} should have processed its Ping"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let received = inbox.try_recv().expect("WASM actor should echo in mixed runtime");
|
||||||
|
assert_eq!(received.0, b"mixed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── alloc alternates between failure and success ────────────────────────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn alloc_alternates_between_failure_and_success() {
|
||||||
|
let wat = r#"
|
||||||
|
(module
|
||||||
|
(import "swactor" "send" (func $send (param i32 i32 i32)))
|
||||||
|
(memory (export "memory") 1)
|
||||||
|
(global $counter (mut i32) (i32.const 0))
|
||||||
|
|
||||||
|
(func (export "alloc") (param $size i32) (result i32)
|
||||||
|
global.get $counter
|
||||||
|
i32.const 1
|
||||||
|
i32.add
|
||||||
|
global.set $counter
|
||||||
|
|
||||||
|
;; Odd calls return -1 (invalid), even calls return 256
|
||||||
|
global.get $counter
|
||||||
|
i32.const 2
|
||||||
|
i32.rem_u
|
||||||
|
i32.const 1
|
||||||
|
i32.eq
|
||||||
|
if (result i32)
|
||||||
|
i32.const -1
|
||||||
|
else
|
||||||
|
i32.const 256
|
||||||
|
end
|
||||||
|
)
|
||||||
|
(func (export "handle") (param $ptr i32) (param $len i32)
|
||||||
|
local.get $ptr
|
||||||
|
local.get $ptr
|
||||||
|
i32.const 32
|
||||||
|
i32.add
|
||||||
|
local.get $len
|
||||||
|
i32.const 32
|
||||||
|
i32.sub
|
||||||
|
call $send
|
||||||
|
)
|
||||||
|
)
|
||||||
|
"#;
|
||||||
|
let wasm = wat::parse_str(wat).unwrap();
|
||||||
|
let engine = SharedEngine::new().unwrap();
|
||||||
|
let actor = WasmActorBuilder::new(engine, wasm).build().unwrap();
|
||||||
|
|
||||||
|
let rt = Runtime::new(RuntimeConfig::default());
|
||||||
|
let inbox = rt.new_inbox::<ByteMessage>().unwrap();
|
||||||
|
let addr = rt.spawn(actor).unwrap();
|
||||||
|
|
||||||
|
// Send 6 messages: 1(fail), 2(ok), 3(fail), 4(ok), 5(fail), 6(ok)
|
||||||
|
let mut echoed = 0;
|
||||||
|
for i in 0u8..6 {
|
||||||
|
rt.send_to(addr, framed_msg(inbox.addr(), &[i])).unwrap();
|
||||||
|
rt.tick();
|
||||||
|
if inbox.try_recv().is_some() {
|
||||||
|
echoed += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(echoed, 3, "should echo on even-numbered alloc calls only");
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue