test: Cycle 56 — interleaved spawn, store16, min/max, runtime drop

- interleaved_spawn_and_message_delivery: spawn-send-spawn-send-tick delivers both
- guest_uses_i32_store16: 16-bit LE store/read
- guest_computes_min_max: select instruction for min/max of two bytes
- drop_runtime_with_live_actors: drop runtime with 10 live WASM actors, no panic

All 240 tests pass. No new bugs found.

Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
This commit is contained in:
Claude 2026-02-13 10:07:22 +00:00
parent 80afb30fb4
commit 8f3710c5f4

View file

@ -8290,3 +8290,119 @@ fn guest_with_nop_instructions() {
let msg = inbox.try_recv().unwrap(); let msg = inbox.try_recv().unwrap();
assert_eq!(msg.0, vec![77], "nops should not affect behavior"); assert_eq!(msg.0, vec![77], "nops should not affect behavior");
} }
// ── Interleaved spawn and message delivery ──────────────────────────────────
#[test]
fn interleaved_spawn_and_message_delivery() {
// Spawn actor, send message, spawn another, send message, tick once.
// Both should process in the same tick.
let engine = SharedEngine::new().unwrap();
let rt = Runtime::new(RuntimeConfig::default());
let inbox = rt.new_inbox::<ByteMessage>().unwrap();
let a1 = rt.spawn(WasmActorBuilder::new(engine.clone(), guest_wasm("echo")).build().unwrap()).unwrap();
rt.send_to(a1, framed_msg(inbox.addr(), b"first")).unwrap();
let a2 = rt.spawn(WasmActorBuilder::new(engine, guest_wasm("echo")).build().unwrap()).unwrap();
rt.send_to(a2, framed_msg(inbox.addr(), b"second")).unwrap();
rt.tick();
let mut msgs: Vec<Vec<u8>> = Vec::new();
while let Some(msg) = inbox.try_recv() {
msgs.push(msg.0);
}
msgs.sort();
assert_eq!(msgs, vec![b"first".to_vec(), b"second".to_vec()]);
}
// ── Guest with i32.store16 ─────────────────────────────────────────────────
#[test]
fn guest_uses_i32_store16() {
let wat = r#"
(module
(import "swactor" "send" (func $send (param i32 i32 i32)))
(memory (export "memory") 1)
(func (export "alloc") (param i32) (result i32) i32.const 4096)
(func (export "handle") (param $ptr i32) (param $len i32)
;; Store 0x1234 as 16-bit LE at offset 200
(i32.store16 (i32.const 200) (i32.const 0x1234))
(call $send (local.get $ptr) (i32.const 200) (i32.const 2))
)
)
"#;
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();
rt.send_to(addr, framed_msg(inbox.addr(), b"s16")).unwrap();
rt.tick();
let msg = inbox.try_recv().unwrap();
assert_eq!(msg.0, vec![0x34, 0x12], "i32.store16 should be little-endian");
}
// ── Guest computes min/max of two payload bytes ─────────────────────────────
#[test]
fn guest_computes_min_max() {
let wat = r#"
(module
(import "swactor" "send" (func $send (param i32 i32 i32)))
(memory (export "memory") 1)
(func (export "alloc") (param i32) (result i32) i32.const 4096)
(func (export "handle") (param $ptr i32) (param $len i32)
(local $a i32) (local $b i32)
(local.set $a (i32.load8_u (i32.add (local.get $ptr) (i32.const 32))))
(local.set $b (i32.load8_u (i32.add (local.get $ptr) (i32.const 33))))
;; min: use select
(i32.store8 (i32.const 200)
(select (local.get $a) (local.get $b)
(i32.lt_u (local.get $a) (local.get $b)))
)
;; max: use select
(i32.store8 (i32.const 201)
(select (local.get $a) (local.get $b)
(i32.gt_u (local.get $a) (local.get $b)))
)
(call $send (local.get $ptr) (i32.const 200) (i32.const 2))
)
)
"#;
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();
rt.send_to(addr, framed_msg(inbox.addr(), &[42, 99])).unwrap();
rt.tick();
let msg = inbox.try_recv().unwrap();
assert_eq!(msg.0[0], 42, "min(42, 99) = 42");
assert_eq!(msg.0[1], 99, "max(42, 99) = 99");
}
// ── Drop runtime with live WASM actors — no leak/panic ──────────────────────
#[test]
fn drop_runtime_with_live_actors() {
let engine = SharedEngine::new().unwrap();
let rt = Runtime::new(RuntimeConfig::default());
for _ in 0..10 {
let actor = WasmActorBuilder::new(engine.clone(), guest_wasm("echo")).build().unwrap();
let addr = rt.spawn(actor).unwrap();
rt.send_to(addr, ByteMessage(vec![1, 2, 3])).unwrap();
}
rt.tick();
// Drop runtime without stopping actors — should not panic or leak
drop(rt);
}