test: Cycle 26 — ptr/len parameter verification, double with empty payload

Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
This commit is contained in:
Claude 2026-02-13 09:27:09 +00:00
parent 4f1fb2cb64
commit 57ff6f1d81

View file

@ -4109,3 +4109,107 @@ fn wasm_actor_watching_another_wasm_actor_doesnt_crash() {
assert!(rt.send_to(addr_a, ByteMessage(vec![1])).is_err());
assert!(rt.send_to(addr_b, ByteMessage(vec![1])).is_err());
}
// ── Guest reads len parameter correctly ─────────────────────────────────────
#[test]
fn guest_receives_correct_len_parameter() {
// Guest stores the len parameter as a 4-byte LE integer at offset 200
// and sends it back. Verifies the host passes the correct length.
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 len at offset 200 as i32
(i32.store (i32.const 200) (local.get $len))
;; Send 4 bytes from offset 200
local.get $ptr
i32.const 200
i32.const 4
call $send
)
)
"#;
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();
// Send a message with 32-byte addr + 10 bytes payload = 42 bytes total
let payload = vec![0u8; 10];
rt.send_to(addr, framed_msg(inbox.addr(), &payload)).unwrap();
rt.tick();
let received = inbox.try_recv().expect("should receive len value");
let len = i32::from_le_bytes(received.0.try_into().unwrap());
assert_eq!(len, 42, "guest should receive total message len (32 addr + 10 payload)");
}
// ── Guest reads ptr parameter correctly ─────────────────────────────────────
#[test]
fn guest_receives_correct_ptr_parameter() {
// Guest stores ptr at offset 200 and sends it back. The ptr should be
// the address returned by alloc (4096 in this case).
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)
(i32.store (i32.const 200) (local.get $ptr))
local.get $ptr
i32.const 200
i32.const 4
call $send
)
)
"#;
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"ptr-check")).unwrap();
rt.tick();
let received = inbox.try_recv().expect("should receive ptr value");
let ptr = i32::from_le_bytes(received.0.try_into().unwrap());
assert_eq!(ptr, 4096, "guest should receive ptr = alloc return value");
}
// ── Double guest with empty payload: sends two zero-length messages ─────────
#[test]
fn double_guest_with_minimal_payload() {
// Double guest with exactly 32 bytes (addr only, no payload).
// Since double checks `len < 32`, a 32-byte message passes the check.
// payload_len = 32 - 32 = 0, so it sends two zero-length messages.
let engine = SharedEngine::new().unwrap();
let actor = WasmActorBuilder::new(engine, guest_wasm("double"))
.build()
.unwrap();
let rt = Runtime::new(RuntimeConfig::default());
let inbox = rt.new_inbox::<ByteMessage>().unwrap();
let addr = rt.spawn(actor).unwrap();
// Send exactly 32 bytes (just the address, no payload)
let msg = ByteMessage(inbox.addr().0.to_vec());
rt.send_to(addr, msg).unwrap();
rt.tick();
let first = inbox.try_recv().expect("double should send first empty message");
let second = inbox.try_recv().expect("double should send second empty message");
assert!(first.0.is_empty(), "payload should be empty");
assert!(second.0.is_empty(), "payload should be empty");
assert!(inbox.try_recv().is_none(), "exactly two messages");
}