From 95f1163251375a9c436f2ad3fdf23ca82e627fe8 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 13 Feb 2026 10:05:26 +0000 Subject: [PATCH] =?UTF-8?q?test:=20Cycle=2054=20=E2=80=94=20grow+send,=202?= =?UTF-8?q?0=C3=9720=20stress,=20minimal=20module,=20data=20segment=20temp?= =?UTF-8?q?late?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - grow_memory_and_use_new_page_for_send: memory.grow then send from new page - twenty_actors_twenty_messages_each: 400 total messages across 20 actors - module_with_only_send_import_needed: minimal sending module works - guest_data_segment_used_as_template: data segment content sent as payload All 232 tests pass. No new bugs found. Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski --- crates/wasm-actor/tests/wasm_actor.rs | 133 ++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/crates/wasm-actor/tests/wasm_actor.rs b/crates/wasm-actor/tests/wasm_actor.rs index f3a8d44..ac08213 100644 --- a/crates/wasm-actor/tests/wasm_actor.rs +++ b/crates/wasm-actor/tests/wasm_actor.rs @@ -8051,4 +8051,137 @@ fn three_level_nested_function_calls() { let msg = inbox.try_recv().unwrap(); assert_eq!(msg.0[0], 36, "(10+2)*3 = 36"); +} + +// ── Guest with grow + use new page for send ───────────────────────────────── + +#[test] +fn grow_memory_and_use_new_page_for_send() { + // Guest grows memory in handle, then uses the new page for the send dest. + 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) + ;; Grow memory by 1 page + (drop (memory.grow (i32.const 1))) + ;; Copy dest address from message to new page (offset 65536) + (memory.copy (i32.const 65536) (local.get $ptr) (i32.const 32)) + ;; Write payload at offset 65600 + (i32.store8 (i32.const 65600) (i32.const 99)) + ;; Send from new page + (call $send (i32.const 65536) (i32.const 65600) (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"grow")).unwrap(); + rt.tick(); + + let msg = inbox.try_recv().expect("send from grown page should work"); + assert_eq!(msg.0, vec![99]); +} + +// ── Stress: 20 actors each processing 20 messages ────────────────────────── + +#[test] +fn twenty_actors_twenty_messages_each() { + let engine = SharedEngine::new().unwrap(); + let rt = Runtime::new(RuntimeConfig::default()); + let inbox = rt.new_inbox::().unwrap(); + + let mut addrs = Vec::new(); + for _ in 0..20 { + let actor = WasmActorBuilder::new(engine.clone(), guest_wasm("echo")).build().unwrap(); + addrs.push(rt.spawn(actor).unwrap()); + } + + for (a, addr) in addrs.iter().enumerate() { + for m in 0u8..20 { + rt.send_to(*addr, framed_msg(inbox.addr(), &[a as u8, m])).unwrap(); + } + } + + // Process across multiple ticks (budget=64 per actor) + for _ in 0..10 { + rt.tick(); + } + + let mut total = 0; + while let Some(_) = inbox.try_recv() { + total += 1; + } + assert_eq!(total, 400, "20 actors × 20 messages = 400 responses"); +} + +// ── Module with multiple imports (only swactor.send matters) ──────────────── + +#[test] +fn module_with_only_send_import_needed() { + // Module only imports swactor.send, no other imports. + // This is the minimal valid module that can send. + 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.store8 (i32.const 200) (i32.const 42)) + (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"min")).unwrap(); + rt.tick(); + + let msg = inbox.try_recv().unwrap(); + assert_eq!(msg.0, vec![42]); +} + +// ── Guest with memory.init-like pattern using data segments ───────────────── + +#[test] +fn guest_data_segment_used_as_template() { + // Guest has a data segment template and sends it as-is. + let wat = r#" + (module + (import "swactor" "send" (func $send (param i32 i32 i32))) + (memory (export "memory") 1) + (data (i32.const 300) "TEMPLATE") + (func (export "alloc") (param i32) (result i32) i32.const 4096) + (func (export "handle") (param $ptr i32) (param $len i32) + ;; Send the data segment content as payload + (call $send (local.get $ptr) (i32.const 300) (i32.const 8)) + ) + ) + "#; + 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"x")).unwrap(); + rt.tick(); + + let msg = inbox.try_recv().unwrap(); + assert_eq!(msg.0, b"TEMPLATE"); } \ No newline at end of file