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