From 6bbfa10dbb35898e55e63269b01331b7a810db7d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 13 Feb 2026 09:59:34 +0000 Subject: [PATCH] =?UTF-8?q?test:=20Cycle=2049=20=E2=80=94=20i32=20store/lo?= =?UTF-8?q?ad,=20eqz,=20silent=201000-msg,=20pre-tick=20delivery?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- crates/wasm-actor/tests/wasm_actor.rs | 115 ++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/crates/wasm-actor/tests/wasm_actor.rs b/crates/wasm-actor/tests/wasm_actor.rs index 6f2d586..e9c6827 100644 --- a/crates/wasm-actor/tests/wasm_actor.rs +++ b/crates/wasm-actor/tests/wasm_actor.rs @@ -7408,4 +7408,119 @@ 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::().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::().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::().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::().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"); } \ No newline at end of file