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:
parent
80afb30fb4
commit
8f3710c5f4
1 changed files with 116 additions and 0 deletions
|
|
@ -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);
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue