From 265a0ba7a7045b5063334f19a26b4b7a7d661d85 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 13 Feb 2026 10:56:32 +0000 Subject: [PATCH] =?UTF-8?q?test(wasm-actor):=20cycle=2086=20=E2=80=94=20co?= =?UTF-8?q?nditional=20double,=20200=20tick=20longevity,=20AND=20mask=20(3?= =?UTF-8?q?83=20tests)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added 4 tests: conditional double or echo, 200 messages across 200 ticks, three inboxes from same runtime, AND mask low nibble. No new bugs found. Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski --- crates/wasm-actor/tests/wasm_actor.rs | 129 ++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/crates/wasm-actor/tests/wasm_actor.rs b/crates/wasm-actor/tests/wasm_actor.rs index 14f931e..bc54501 100644 --- a/crates/wasm-actor/tests/wasm_actor.rs +++ b/crates/wasm-actor/tests/wasm_actor.rs @@ -12220,4 +12220,133 @@ fn budget_leaves_remaining_for_next_tick() { assert_eq!(t1 + t2 + t3, 12, "all 12 processed across ticks"); assert!(t1 <= 5, "budget limits first tick"); +} + +// ── Cycle 86 ───────────────────────────────────────────────────────────────── + +// Guest doubles only specific payloads, echoes others +#[test] +fn guest_conditional_double_or_echo() { + 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) + (local $payload_len i32) + (if (i32.lt_u (local.get $len) (i32.const 33)) (then return)) + (local.set $payload_len (i32.sub (local.get $len) (i32.const 32))) + ;; If first payload byte is 0xDD, double it + (if (i32.eq (i32.load8_u (i32.add (local.get $ptr) (i32.const 32))) (i32.const 0xDD)) + (then + ;; Send twice + (call $send (local.get $ptr) (i32.add (local.get $ptr) (i32.const 32)) (local.get $payload_len)) + (call $send (local.get $ptr) (i32.add (local.get $ptr) (i32.const 32)) (local.get $payload_len)) + ) + (else + ;; Echo once + (call $send (local.get $ptr) (i32.add (local.get $ptr) (i32.const 32)) (local.get $payload_len)) + ) + ) + ) + )"#; + 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(); + + // Regular message — echo + rt.send_to(addr, framed_msg(inbox.addr(), &[0xAA, 0xBB])).unwrap(); + // Trigger double + rt.send_to(addr, framed_msg(inbox.addr(), &[0xDD, 0xEE])).unwrap(); + rt.tick(); + + let msgs: Vec> = std::iter::from_fn(|| inbox.try_recv().map(|m| m.0)).collect(); + assert_eq!(msgs.len(), 3, "1 echo + 2 double = 3"); +} + +// Long-running WASM actor: 200 messages across 200 ticks +#[test] +fn two_hundred_messages_across_two_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..200 { + rt.send_to(addr, framed_msg(inbox.addr(), &[i])).unwrap(); + rt.tick(); + let resp = inbox.try_recv().expect("each tick echoes"); + assert_eq!(resp.0, vec![i]); + } +} + +// Multiple inboxes from same runtime +#[test] +fn three_inboxes_from_same_runtime() { + let engine = SharedEngine::new().unwrap(); + let rt = Runtime::new(RuntimeConfig::default()); + let inbox1 = rt.new_inbox::().unwrap(); + let inbox2 = rt.new_inbox::().unwrap(); + let inbox3 = rt.new_inbox::().unwrap(); + + let actor = WasmActorBuilder::new(engine, guest_wasm("echo")).build().unwrap(); + let addr = rt.spawn(actor).unwrap(); + + // Send to echo targeting different inboxes + rt.send_to(addr, framed_msg(inbox1.addr(), b"one")).unwrap(); + rt.tick(); + rt.send_to(addr, framed_msg(inbox2.addr(), b"two")).unwrap(); + rt.tick(); + rt.send_to(addr, framed_msg(inbox3.addr(), b"three")).unwrap(); + rt.tick(); + + assert_eq!(inbox1.try_recv().unwrap().0, b"one"); + assert_eq!(inbox2.try_recv().unwrap().0, b"two"); + assert_eq!(inbox3.try_recv().unwrap().0, b"three"); +} + +// Guest that uses i32.and to mask bytes +#[test] +fn guest_and_mask_low_nibble() { + 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) + (local $i i32) + (if (i32.lt_u (local.get $len) (i32.const 33)) (then return)) + ;; Mask each payload byte to low nibble + (local.set $i (i32.const 32)) + (block $exit + (loop $loop + (br_if $exit (i32.ge_u (local.get $i) (local.get $len))) + (i32.store8 + (i32.add (local.get $ptr) (local.get $i)) + (i32.and + (i32.load8_u (i32.add (local.get $ptr) (local.get $i))) + (i32.const 0x0F))) + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (br $loop) + ) + ) + (call $send (local.get $ptr) + (i32.add (local.get $ptr) (i32.const 32)) + (i32.sub (local.get $len) (i32.const 32))) + ) + )"#; + 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(); + + rt.send_to(addr, framed_msg(inbox.addr(), &[0xAB, 0xCD, 0xEF])).unwrap(); + rt.tick(); + + let resp = inbox.try_recv().expect("masked response"); + assert_eq!(resp.0, vec![0x0B, 0x0D, 0x0F]); } \ No newline at end of file