test: Cycle 49 — i32 store/load, eqz, silent 1000-msg, pre-tick delivery
- guest_uses_i32_store_load: verifies little-endian i32 store/load - guest_uses_eqz: i32.eqz instruction for empty/non-empty detection - silent_guest_processes_1000_messages: silent actor handles 1000 messages, stays alive - send_before_first_tick_delivers: send immediately after spawn delivers on first tick All 212 tests pass. No new bugs found. Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
This commit is contained in:
parent
d4961978ec
commit
6bbfa10dbb
1 changed files with 115 additions and 0 deletions
|
|
@ -7409,3 +7409,118 @@ fn stop_every_other_actor_remaining_work() {
|
|||
received.sort();
|
||||
assert_eq!(received, vec![1, 3, 5, 7, 9], "only odd-indexed actors should respond");
|
||||
}
|
||||
|
||||
// ── Guest uses i32.store/load (32-bit) for response ─────────────────────────
|
||||
|
||||
#[test]
|
||||
fn guest_uses_i32_store_load() {
|
||||
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 0x04030201 at offset 200 (little-endian: 01 02 03 04)
|
||||
(i32.store (i32.const 200) (i32.const 0x04030201))
|
||||
(call $send (local.get $ptr) (i32.const 200) (i32.const 4))
|
||||
)
|
||||
)
|
||||
"#;
|
||||
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"le")).unwrap();
|
||||
rt.tick();
|
||||
|
||||
let msg = inbox.try_recv().unwrap();
|
||||
assert_eq!(msg.0, vec![0x01, 0x02, 0x03, 0x04], "i32.store is little-endian");
|
||||
}
|
||||
|
||||
// ── Guest with i32.eqz instruction ─────────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn guest_uses_eqz() {
|
||||
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 $payload_len i32)
|
||||
(local.set $payload_len (i32.sub (local.get $len) (i32.const 32)))
|
||||
;; eqz: 1 if payload_len == 0, else 0
|
||||
(i32.store8 (i32.const 200) (i32.eqz (local.get $payload_len)))
|
||||
(call $send (local.get $ptr) (i32.const 200) (i32.const 1))
|
||||
)
|
||||
)
|
||||
"#;
|
||||
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"")).unwrap(); // empty payload
|
||||
rt.send_to(addr, framed_msg(inbox.addr(), b"x")).unwrap(); // non-empty
|
||||
rt.tick();
|
||||
|
||||
let msg1 = inbox.try_recv().unwrap();
|
||||
let msg2 = inbox.try_recv().unwrap();
|
||||
assert_eq!(msg1.0[0], 1, "empty payload → eqz=1");
|
||||
assert_eq!(msg2.0[0], 0, "non-empty payload → eqz=0");
|
||||
}
|
||||
|
||||
// ── Silent guest processes many messages without any observable effect ───────
|
||||
|
||||
#[test]
|
||||
fn silent_guest_processes_1000_messages() {
|
||||
let engine = SharedEngine::new().unwrap();
|
||||
let actor = WasmActorBuilder::new(engine, guest_wasm("silent")).build().unwrap();
|
||||
|
||||
let rt = Runtime::new(RuntimeConfig::default());
|
||||
let inbox = rt.new_inbox::<ByteMessage>().unwrap();
|
||||
let addr = rt.spawn(actor).unwrap();
|
||||
|
||||
for _ in 0..1000 {
|
||||
rt.send_to(addr, ByteMessage(vec![0xFF; 100])).unwrap();
|
||||
}
|
||||
|
||||
// Process across multiple ticks (budget = 64 default)
|
||||
for _ in 0..20 {
|
||||
rt.tick();
|
||||
}
|
||||
|
||||
assert!(inbox.try_recv().is_none(), "silent never sends");
|
||||
|
||||
// Actor should still be alive
|
||||
rt.send_to(addr, framed_msg(inbox.addr(), b"still-here")).unwrap();
|
||||
rt.tick();
|
||||
// Still no response from silent
|
||||
assert!(inbox.try_recv().is_none());
|
||||
}
|
||||
|
||||
// ── Build actor, send before spawn — verify spawn then send works ───────────
|
||||
|
||||
#[test]
|
||||
fn send_before_first_tick_delivers() {
|
||||
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();
|
||||
|
||||
// Send immediately after spawn, before any tick
|
||||
rt.send_to(addr, framed_msg(inbox.addr(), b"pre-tick")).unwrap();
|
||||
rt.tick();
|
||||
|
||||
let msg = inbox.try_recv().expect("message sent before first tick should deliver");
|
||||
assert_eq!(msg.0, b"pre-tick");
|
||||
}
|
||||
Loading…
Reference in a new issue