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:
parent
f0751163c8
commit
4e743479ee
1 changed files with 133 additions and 0 deletions
|
|
@ -11002,4 +11002,137 @@ fn echo_after_hundred_idle_ticks() {
|
|||
rt.send_to(addr, framed_msg(inbox.addr(), b"after")).unwrap();
|
||||
rt.tick();
|
||||
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");
|
||||
}
|
||||
Loading…
Reference in a new issue