From e96891ae89468433f1e2eab5dd94666fa36690c5 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 13 Feb 2026 10:44:09 +0000 Subject: [PATCH] =?UTF-8?q?test(wasm-actor):=20cycle=2078=20=E2=80=94=20fi?= =?UTF-8?q?bonacci=20recursion,=205-actor=20inbox,=20block=20result=20(346?= =?UTF-8?q?=20tests)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added 5 tests: fibonacci via recursion, single inbox from five actors, minimal lifecycle no messages, reuse message bytes, block with result value. No new bugs found. Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski --- crates/wasm-actor/tests/wasm_actor.rs | 119 ++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/crates/wasm-actor/tests/wasm_actor.rs b/crates/wasm-actor/tests/wasm_actor.rs index a84860f..0290749 100644 --- a/crates/wasm-actor/tests/wasm_actor.rs +++ b/crates/wasm-actor/tests/wasm_actor.rs @@ -11258,4 +11258,123 @@ fn actor_replacement_cycle() { rt.stop_actor(addr); rt.tick(); } +} + +// ── Cycle 78 ───────────────────────────────────────────────────────────────── + +// Guest with recursive helper that computes fibonacci (bounded) +#[test] +fn guest_fibonacci_via_recursion() { + let wat = r#"(module + (memory (export "memory") 1) + (func $fib (param $n i32) (result i32) + (if (result i32) (i32.le_u (local.get $n) (i32.const 1)) + (then (local.get $n)) + (else + (i32.add + (call $fib (i32.sub (local.get $n) (i32.const 1))) + (call $fib (i32.sub (local.get $n) (i32.const 2))) + ) + ) + ) + ) + (func (export "alloc") (param $len i32) (result i32) i32.const 1024) + (func (export "handle") (param $ptr i32) (param $len i32) + ;; Compute fib(10) = 55, store at ptr + (i32.store (local.get $ptr) (call $fib (i32.const 10))) + ) + )"#; + let engine = SharedEngine::new().unwrap(); + let actor = WasmActorBuilder::new(engine, wat::parse_str(wat).unwrap()) + .build().unwrap(); + let rt = Runtime::new(RuntimeConfig::default()); + let addr = rt.spawn(actor).unwrap(); + rt.send_to(addr, ByteMessage(vec![0u8; 4])).unwrap(); + rt.tick(); +} + +// Inbox receives messages from multiple WASM actors simultaneously +#[test] +fn single_inbox_receives_from_five_actors() { + let engine = SharedEngine::new().unwrap(); + let rt = Runtime::new(RuntimeConfig::default()); + let inbox = rt.new_inbox::().unwrap(); + + for i in 0..5u8 { + let actor = WasmActorBuilder::new(engine.clone(), guest_wasm("echo")).build().unwrap(); + let addr = rt.spawn(actor).unwrap(); + rt.send_to(addr, framed_msg(inbox.addr(), &[i])).unwrap(); + } + rt.tick(); + + let mut msgs: Vec> = std::iter::from_fn(|| inbox.try_recv().map(|m| m.0)).collect(); + msgs.sort(); + assert_eq!(msgs.len(), 5); + for i in 0..5u8 { + assert_eq!(msgs[i as usize], vec![i]); + } +} + +// Guest that does nothing at all — purely exercises spawn+tick+stop lifecycle +#[test] +fn minimal_lifecycle_no_messages() { + let wat = r#"(module + (memory (export "memory") 1) + (func (export "alloc") (param $len i32) (result i32) i32.const 0) + (func (export "handle") (param $ptr i32) (param $len i32)) + )"#; + let engine = SharedEngine::new().unwrap(); + let actor = WasmActorBuilder::new(engine, wat::parse_str(wat).unwrap()) + .build().unwrap(); + let rt = Runtime::new(RuntimeConfig::default()); + let addr = rt.spawn(actor).unwrap(); + rt.tick(); + rt.tick(); + rt.stop_actor(addr); + rt.tick(); +} + +// Same message bytes reused for multiple sends — no aliasing issues +#[test] +fn reuse_message_bytes_for_multiple_sends() { + 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(); + + let msg = framed_msg(inbox.addr(), b"reuse"); + for _ in 0..5 { + // Clone the same message each time + rt.send_to(addr, msg.clone()).unwrap(); + } + rt.tick(); + + let msgs: Vec> = std::iter::from_fn(|| inbox.try_recv().map(|m| m.0)).collect(); + assert_eq!(msgs.len(), 5); + assert!(msgs.iter().all(|m| m == b"reuse")); +} + +// Guest uses block with result value +#[test] +fn guest_block_with_result_value() { + let wat = r#"(module + (memory (export "memory") 1) + (func (export "alloc") (param $len i32) (result i32) i32.const 1024) + (func (export "handle") (param $ptr i32) (param $len i32) + ;; Block that produces a value + (i32.store (local.get $ptr) + (block (result i32) + (i32.add (local.get $len) (i32.const 42)) + ) + ) + ) + )"#; + let engine = SharedEngine::new().unwrap(); + let actor = WasmActorBuilder::new(engine, wat::parse_str(wat).unwrap()) + .build().unwrap(); + let rt = Runtime::new(RuntimeConfig::default()); + let addr = rt.spawn(actor).unwrap(); + rt.send_to(addr, ByteMessage(vec![0u8; 4])).unwrap(); + rt.tick(); } \ No newline at end of file