test: Cycle 50 — modulo, 8KB echo, duplicate sends, full lifecycle fuzz
- guest_uses_modulo: i32.rem_u instruction for mod 10 computation - echo_8kb_payload: 8192-byte payload round-trips with pattern check - duplicate_sends_all_deliver: 5 identical messages all deliver - prop_full_lifecycle_never_panics: combined fuzz (1-5 actors, 0-10 msgs, any byte) All 216 tests pass (14 property tests). No new bugs found. Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
This commit is contained in:
parent
6bbfa10dbb
commit
98061c9464
1 changed files with 120 additions and 0 deletions
|
|
@ -7523,4 +7523,124 @@ fn send_before_first_tick_delivers() {
|
||||||
|
|
||||||
let msg = inbox.try_recv().expect("message sent before first tick should deliver");
|
let msg = inbox.try_recv().expect("message sent before first tick should deliver");
|
||||||
assert_eq!(msg.0, b"pre-tick");
|
assert_eq!(msg.0, b"pre-tick");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Guest with i32.rem_u (modulo) ──────────────────────────────────────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn guest_uses_modulo() {
|
||||||
|
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)
|
||||||
|
;; Read first payload byte, compute mod 10
|
||||||
|
(i32.store8 (i32.const 200)
|
||||||
|
(i32.rem_u
|
||||||
|
(i32.load8_u (i32.add (local.get $ptr) (i32.const 32)))
|
||||||
|
(i32.const 10)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(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::<ByteMessage>().unwrap();
|
||||||
|
let addr = rt.spawn(actor).unwrap();
|
||||||
|
|
||||||
|
rt.send_to(addr, framed_msg(inbox.addr(), &[47])).unwrap(); // 47 % 10 = 7
|
||||||
|
rt.send_to(addr, framed_msg(inbox.addr(), &[100])).unwrap(); // 100 % 10 = 0
|
||||||
|
rt.send_to(addr, framed_msg(inbox.addr(), &[3])).unwrap(); // 3 % 10 = 3
|
||||||
|
rt.tick();
|
||||||
|
|
||||||
|
let results: Vec<u8> = std::iter::from_fn(|| inbox.try_recv().map(|m| m.0[0])).collect();
|
||||||
|
assert_eq!(results, vec![7, 0, 3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Echo with 8KB payload (tests larger than single page alloc) ─────────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn echo_8kb_payload() {
|
||||||
|
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::<ByteMessage>().unwrap();
|
||||||
|
let addr = rt.spawn(actor).unwrap();
|
||||||
|
|
||||||
|
let payload: Vec<u8> = (0..8192).map(|i| (i % 251) as u8).collect();
|
||||||
|
rt.send_to(addr, framed_msg(inbox.addr(), &payload)).unwrap();
|
||||||
|
rt.tick();
|
||||||
|
|
||||||
|
let msg = inbox.try_recv().unwrap();
|
||||||
|
assert_eq!(msg.0.len(), 8192);
|
||||||
|
assert_eq!(msg.0, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Multiple sends with exact same payload to same dest ─────────────────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn duplicate_sends_all_deliver() {
|
||||||
|
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::<ByteMessage>().unwrap();
|
||||||
|
let addr = rt.spawn(actor).unwrap();
|
||||||
|
|
||||||
|
// Send the exact same message 5 times
|
||||||
|
for _ in 0..5 {
|
||||||
|
rt.send_to(addr, framed_msg(inbox.addr(), b"dup")).unwrap();
|
||||||
|
}
|
||||||
|
rt.tick();
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
while let Some(msg) = inbox.try_recv() {
|
||||||
|
assert_eq!(msg.0, b"dup");
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
assert_eq!(count, 5, "all 5 duplicate sends should deliver");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Property: build + tick + stop cycle never leaks (combined fuzz) ──────────
|
||||||
|
|
||||||
|
proptest! {
|
||||||
|
#[test]
|
||||||
|
fn prop_full_lifecycle_never_panics(
|
||||||
|
n_actors in 1u8..5,
|
||||||
|
n_msgs in 0u8..10,
|
||||||
|
payload_byte in proptest::num::u8::ANY,
|
||||||
|
) {
|
||||||
|
let engine = SharedEngine::new().unwrap();
|
||||||
|
let rt = Runtime::new(RuntimeConfig::default());
|
||||||
|
let inbox = rt.new_inbox::<ByteMessage>().unwrap();
|
||||||
|
|
||||||
|
let mut addrs = Vec::new();
|
||||||
|
for _ in 0..n_actors {
|
||||||
|
let actor = WasmActorBuilder::new(engine.clone(), guest_wasm("echo")).build().unwrap();
|
||||||
|
addrs.push(rt.spawn(actor).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
for addr in &addrs {
|
||||||
|
for _ in 0..n_msgs {
|
||||||
|
let _ = rt.send_to(*addr, framed_msg(inbox.addr(), &[payload_byte]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rt.tick();
|
||||||
|
rt.tick();
|
||||||
|
|
||||||
|
for addr in &addrs {
|
||||||
|
rt.stop_actor(*addr);
|
||||||
|
}
|
||||||
|
rt.tick();
|
||||||
|
rt.tick();
|
||||||
|
|
||||||
|
while let Some(_) = inbox.try_recv() {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue