test(wasm-actor): cycle 76 — dual-address send, 500 messages, early return (337 tests)

Added 4 tests: guest sends to two addresses in one handle, engine clone builds
independent actors, 500 messages in one tick, guest early return from handle.
No new bugs found.

Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
This commit is contained in:
Claude 2026-02-13 10:41:31 +00:00
parent f0751163c8
commit 4e743479ee

View file

@ -11003,3 +11003,136 @@ fn echo_after_hundred_idle_ticks() {
rt.tick(); rt.tick();
assert_eq!(inbox.try_recv().unwrap().0, b"after"); assert_eq!(inbox.try_recv().unwrap().0, b"after");
} }
// ── Cycle 76 ─────────────────────────────────────────────────────────────────
// Guest with complex send pattern: send to two different addresses in one handle
#[test]
fn guest_sends_to_two_addresses_in_one_handle() {
let wat = r#"(module
(import "swactor" "send" (func $send (param i32 i32 i32)))
(memory (export "memory") 1)
(func (export "alloc") (param $len i32) (result i32) i32.const 1024)
(func (export "handle") (param $ptr i32) (param $len i32)
;; Message layout: [addr1:32][addr2:32][payload:rest]
;; Send payload to both addresses
(if (i32.ge_u (local.get $len) (i32.const 65))
(then
;; Send to addr1
(call $send
(local.get $ptr)
(i32.add (local.get $ptr) (i32.const 64))
(i32.sub (local.get $len) (i32.const 64))
)
;; Send to addr2
(call $send
(i32.add (local.get $ptr) (i32.const 32))
(i32.add (local.get $ptr) (i32.const 64))
(i32.sub (local.get $len) (i32.const 64))
)
)
)
)
)"#;
let engine = SharedEngine::new().unwrap();
let actor = WasmActorBuilder::new(engine, wat::parse_str(wat).unwrap())
.build().unwrap();
let rt = Runtime::new(RuntimeConfig::default());
let inbox1 = rt.new_inbox::<ByteMessage>().unwrap();
let inbox2 = rt.new_inbox::<ByteMessage>().unwrap();
let addr = rt.spawn(actor).unwrap();
let mut msg = Vec::new();
msg.extend_from_slice(&inbox1.addr().0); // addr1
msg.extend_from_slice(&inbox2.addr().0); // addr2
msg.extend_from_slice(b"shared-data"); // payload
rt.send_to(addr, ByteMessage(msg)).unwrap();
rt.tick();
let r1 = inbox1.try_recv().expect("inbox1 should receive");
let r2 = inbox2.try_recv().expect("inbox2 should receive");
assert_eq!(r1.0, b"shared-data");
assert_eq!(r2.0, b"shared-data");
}
// Build actor, clone engine, build another actor — verify independence
#[test]
fn engine_clone_builds_independent_actors() {
let engine = SharedEngine::new().unwrap();
let clone = engine.clone();
let a1 = WasmActorBuilder::new(engine, guest_wasm("echo")).build().unwrap();
let a2 = WasmActorBuilder::new(clone, guest_wasm("double")).build().unwrap();
let rt = Runtime::new(RuntimeConfig::default());
let inbox = rt.new_inbox::<ByteMessage>().unwrap();
let addr1 = rt.spawn(a1).unwrap();
let addr2 = rt.spawn(a2).unwrap();
rt.send_to(addr1, framed_msg(inbox.addr(), b"A")).unwrap();
rt.send_to(addr2, framed_msg(inbox.addr(), b"B")).unwrap();
rt.tick();
let msgs: Vec<Vec<u8>> = std::iter::from_fn(|| inbox.try_recv().map(|m| m.0)).collect();
assert_eq!(msgs.len(), 3, "1 echo + 2 double = 3");
}
// Guest handles 500 messages in a single tick
#[test]
fn five_hundred_messages_in_one_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();
for i in 0..500u16 {
let payload = i.to_le_bytes().to_vec();
rt.send_to(addr, framed_msg(inbox.addr(), &payload)).unwrap();
}
// May need multiple ticks depending on budget
for _ in 0..20 {
rt.tick();
}
let msgs: Vec<_> = std::iter::from_fn(|| inbox.try_recv()).collect();
assert_eq!(msgs.len(), 500, "all 500 messages echoed");
}
// Guest with return in the middle of handle — early exit
#[test]
fn guest_early_return_from_handle() {
let wat = r#"(module
(import "swactor" "send" (func $send (param i32 i32 i32)))
(memory (export "memory") 1)
(func (export "alloc") (param $len i32) (result i32) i32.const 1024)
(func (export "handle") (param $ptr i32) (param $len i32)
;; If len < 33, return early (no send)
(if (i32.lt_u (local.get $len) (i32.const 33))
(then return)
)
;; Only reaches here for long messages
(call $send
(local.get $ptr)
(i32.add (local.get $ptr) (i32.const 32))
(i32.sub (local.get $len) (i32.const 32))
)
)
)"#;
let engine = SharedEngine::new().unwrap();
let actor = WasmActorBuilder::new(engine, wat::parse_str(wat).unwrap())
.build().unwrap();
let rt = Runtime::new(RuntimeConfig::default());
let inbox = rt.new_inbox::<ByteMessage>().unwrap();
let addr = rt.spawn(actor).unwrap();
// Short message — no response
rt.send_to(addr, ByteMessage(vec![0u8; 10])).unwrap();
rt.tick();
assert!(inbox.try_recv().is_none());
// Long message — response
rt.send_to(addr, framed_msg(inbox.addr(), b"long-enough")).unwrap();
rt.tick();
let resp = inbox.try_recv().expect("long message should echo");
assert_eq!(resp.0, b"long-enough");
}