From d68bb17a39f4a845a070ff658f7b332b84d264ad Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 13 Feb 2026 09:17:53 +0000 Subject: [PATCH] =?UTF-8?q?test:=20Cycle=2020=20=E2=80=94=20memory.copy,?= =?UTF-8?q?=2050-actor=20engine=20stress,=20payload=20integrity,=20lifecyc?= =?UTF-8?q?le=20fuzz?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski --- crates/wasm-actor/tests/wasm_actor.rs | 134 ++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/crates/wasm-actor/tests/wasm_actor.rs b/crates/wasm-actor/tests/wasm_actor.rs index 958f79b..c967876 100644 --- a/crates/wasm-actor/tests/wasm_actor.rs +++ b/crates/wasm-actor/tests/wasm_actor.rs @@ -3452,3 +3452,137 @@ fn hot_swap_wasm_actor_works() { assert_eq!(second.0, b"double-phase"); assert!(inbox.try_recv().is_none()); } + +// ── Guest uses memory.copy: bulk copy within linear memory ────────────────── + +#[test] +fn guest_using_memory_copy_works() { + 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 256) + (func (export "handle") (param $ptr i32) (param $len i32) + (i32.store8 (i32.const 500) (i32.const 72)) + (i32.store8 (i32.const 501) (i32.const 73)) + (memory.copy (i32.const 600) (i32.const 500) (i32.const 2)) + local.get $ptr + i32.const 600 + i32.const 2 + call $send + ) + ) + "#; + 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"copy-test")).unwrap(); + rt.tick(); + + let received = inbox.try_recv().expect("memory.copy should work"); + assert_eq!(received.0, b"HI"); +} + +// ── Engine clone stress: 50 actors from same engine ───────────────────────── + +#[test] +fn fifty_actors_from_same_engine() { + let engine = SharedEngine::new().unwrap(); + let wasm_bytes = guest_wasm("echo"); + let rt = Runtime::new(RuntimeConfig::default()); + let inbox = rt.new_inbox::().unwrap(); + + let mut addrs = Vec::new(); + for _ in 0..50 { + let actor = WasmActorBuilder::new(engine.clone(), wasm_bytes.clone()) + .build() + .unwrap(); + addrs.push(rt.spawn(actor).unwrap()); + } + + rt.send_to(addrs[0], framed_msg(inbox.addr(), b"first")).unwrap(); + rt.send_to(addrs[49], framed_msg(inbox.addr(), b"last")).unwrap(); + rt.tick(); + + let mut received = Vec::new(); + while let Some(msg) = inbox.try_recv() { + received.push(msg.0.clone()); + } + assert_eq!(received.len(), 2); + assert!(received.contains(&b"first".to_vec())); + assert!(received.contains(&b"last".to_vec())); +} + +// ── Payload integrity: pattern check for copy correctness ─────────────────── + +#[test] +fn payload_pattern_integrity_check() { + 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 payload: Vec = (0..500).map(|i| ((i * 7 + 13) % 256) as u8).collect(); + rt.send_to(addr, framed_msg(inbox.addr(), &payload)).unwrap(); + rt.tick(); + + let received = inbox.try_recv().expect("pattern payload should echo"); + assert_eq!(received.0, payload, "payload integrity check"); +} + +// ── Lifecycle fuzz: echo with random stopping ─────────────────────────────── + +proptest! { + #[test] + fn prop_echo_lifecycle_fuzz( + num_messages in 1usize..30, + payload_sizes in proptest::collection::vec(1usize..200, 1..30), + stop_at in proptest::option::of(0usize..30), + ) { + 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_count = num_messages.min(payload_sizes.len()); + let mut echoed = 0; + + for i in 0..msg_count { + if stop_at == Some(i) { + let _ = rt.stop_actor(addr); + rt.tick(); + rt.tick(); + break; + } + + let payload: Vec = (0..payload_sizes[i]).map(|j| (j % 256) as u8).collect(); + let send_result = rt.send_to(addr, framed_msg(inbox.addr(), &payload)); + if send_result.is_err() { + break; + } + rt.tick(); + + if let Some(received) = inbox.try_recv() { + prop_assert_eq!(received.0, payload); + echoed += 1; + } + } + + if stop_at.is_none() || stop_at.unwrap_or(0) > 0 { + prop_assert!(echoed > 0 || stop_at == Some(0)); + } + } +}