From d76d6e0819ad19cb1b35e4f6884818b65d0b1469 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 13 Feb 2026 10:23:52 +0000 Subject: [PATCH] =?UTF-8?q?test(wasm-actor):=20cycle=2066=20=E2=80=94=2010?= =?UTF-8?q?0=20tick=20longevity,=20nested=20if/else,=20engine=20drop=20(28?= =?UTF-8?q?9=20tests)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added 5 tests: logical right shift, 100 messages across 100 ticks, three-deep nested if/else, increasing payload sends in one handle, engine dropped after build. 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 1c6a13f..afd6868 100644 --- a/crates/wasm-actor/tests/wasm_actor.rs +++ b/crates/wasm-actor/tests/wasm_actor.rs @@ -9622,4 +9622,137 @@ fn two_actors_from_cloned_wasm_bytes() { assert_eq!(inbox1.try_recv().unwrap().0, b"one"); assert_eq!(inbox2.try_recv().unwrap().0, b"two"); +} + +// ── Cycle 66 ───────────────────────────────────────────────────────────────── + +// Guest uses i32.shr_u (logical right shift) +#[test] +fn guest_uses_logical_right_shift() { + 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) + ;; Load 4 bytes from ptr, shift right by 4, store back + (i32.store (local.get $ptr) + (i32.shr_u (i32.load (local.get $ptr)) (i32.const 4)) + ) + ) + )"#; + 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![0xFF, 0x00, 0x00, 0x00])).unwrap(); + rt.tick(); +} + +// Long-lived actor: 100 messages across 100 ticks +#[test] +fn hundred_messages_across_hundred_ticks() { + 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(); + + for i in 0u8..100 { + rt.send_to(addr, framed_msg(inbox.addr(), &[i])).unwrap(); + rt.tick(); + let resp = inbox.try_recv().expect("each tick should echo"); + assert_eq!(resp.0, vec![i], "tick {i} content mismatch"); + } +} + +// Guest with nested if/else chains +#[test] +fn guest_nested_if_else_three_deep() { + 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) + (if (i32.gt_u (local.get $len) (i32.const 10)) + (then + (if (i32.gt_u (local.get $len) (i32.const 20)) + (then + (if (i32.gt_u (local.get $len) (i32.const 30)) + (then (i32.store (local.get $ptr) (i32.const 3))) + (else (i32.store (local.get $ptr) (i32.const 2))) + ) + ) + (else (i32.store (local.get $ptr) (i32.const 1))) + ) + ) + (else (i32.store (local.get $ptr) (i32.const 0))) + ) + ) + )"#; + 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(); + + // len=5 → branch 0, len=15 → branch 1, len=25 → branch 2, len=35 → branch 3 + for len in [5, 15, 25, 35] { + rt.send_to(addr, ByteMessage(vec![0u8; len])).unwrap(); + } + rt.tick(); // no trap in any branch +} + +// Multiple sends in one handle with increasing payload sizes +#[test] +fn guest_sends_increasing_payloads_in_one_handle() { + let wat = r#"(module + (import "swactor" "send" (func $send (param i32 i32 i32))) + (memory (export "memory") 1) + (func (export "alloc") (param $len i32) (result i32) i32.const 1024) + (func (export "handle") (param $ptr i32) (param $len i32) + ;; Send 1 byte, 2 bytes, 3 bytes from offset 0 (with dest at ptr) + (if (i32.ge_u (local.get $len) (i32.const 32)) + (then + (call $send (local.get $ptr) (i32.add (local.get $ptr) (i32.const 32)) (i32.const 1)) + (call $send (local.get $ptr) (i32.add (local.get $ptr) (i32.const 32)) (i32.const 2)) + (call $send (local.get $ptr) (i32.add (local.get $ptr) (i32.const 32)) (i32.const 3)) + ) + ) + ) + )"#; + let engine = SharedEngine::new().unwrap(); + let actor = WasmActorBuilder::new(engine, wat::parse_str(wat).unwrap()) + .build().unwrap(); + let rt = Runtime::new(RuntimeConfig::default()); + let inbox = rt.new_inbox::().unwrap(); + let addr = rt.spawn(actor).unwrap(); + + let mut msg = Vec::new(); + msg.extend_from_slice(&inbox.addr().0); + msg.extend_from_slice(b"ABCDE"); + rt.send_to(addr, ByteMessage(msg)).unwrap(); + rt.tick(); + + let msgs: Vec> = std::iter::from_fn(|| inbox.try_recv().map(|m| m.0)).collect(); + assert_eq!(msgs.len(), 3); + assert_eq!(msgs[0].len(), 1); + assert_eq!(msgs[1].len(), 2); + assert_eq!(msgs[2].len(), 3); +} + +// Engine and builder are independent — dropping engine after build still works +#[test] +fn engine_dropped_after_build_actor_still_works() { + let actor = { + let engine = SharedEngine::new().unwrap(); + WasmActorBuilder::new(engine, guest_wasm("echo")).build().unwrap() + // engine dropped here + }; + 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"after-drop")).unwrap(); + rt.tick(); + + let resp = inbox.try_recv().expect("should echo after engine dropped"); + assert_eq!(resp.0, b"after-drop"); } \ No newline at end of file