test(wasm-actor): cycle 87 — OR mask, 500 actors, bitwise fuzz, last byte mod (389 tests)
Added 6 tests: OR set high nibble, 31-byte message too short, loop to 1000, 500 actors one message each, bitwise identical property test, modify last byte. No new bugs found. Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
This commit is contained in:
parent
265a0ba7a7
commit
1a6ee63d03
1 changed files with 167 additions and 0 deletions
|
|
@ -12349,4 +12349,171 @@ fn guest_and_mask_low_nibble() {
|
|||
|
||||
let resp = inbox.try_recv().expect("masked response");
|
||||
assert_eq!(resp.0, vec![0x0B, 0x0D, 0x0F]);
|
||||
}
|
||||
|
||||
// ── Cycle 87 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
// Guest with i32.or to set high bits
|
||||
#[test]
|
||||
fn guest_or_set_high_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))
|
||||
;; OR each payload byte with 0xF0
|
||||
(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.or
|
||||
(i32.load8_u (i32.add (local.get $ptr) (local.get $i)))
|
||||
(i32.const 0xF0)))
|
||||
(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::<ByteMessage>().unwrap();
|
||||
let addr = rt.spawn(actor).unwrap();
|
||||
|
||||
rt.send_to(addr, framed_msg(inbox.addr(), &[0x01, 0x02, 0x03])).unwrap();
|
||||
rt.tick();
|
||||
|
||||
let resp = inbox.try_recv().expect("ORed response");
|
||||
assert_eq!(resp.0, vec![0xF1, 0xF2, 0xF3]);
|
||||
}
|
||||
|
||||
// Guest handles exactly 31-byte message (1 byte less than address frame)
|
||||
#[test]
|
||||
fn echo_with_thirty_one_byte_message() {
|
||||
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();
|
||||
|
||||
// 31 bytes: not enough for a full address frame (32 bytes) — echo won't send
|
||||
rt.send_to(addr, ByteMessage(vec![0xAA; 31])).unwrap();
|
||||
rt.tick();
|
||||
// Echo needs >= 32 bytes for dest address, so nothing should be sent
|
||||
assert!(inbox.try_recv().is_none(), "31-byte message too short for echo framing");
|
||||
}
|
||||
|
||||
// Guest with loop that counts to 1000
|
||||
#[test]
|
||||
fn guest_loop_counts_to_thousand() {
|
||||
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)
|
||||
(local $i i32)
|
||||
(local.set $i (i32.const 0))
|
||||
(block $exit
|
||||
(loop $loop
|
||||
(br_if $exit (i32.ge_u (local.get $i) (i32.const 1000)))
|
||||
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
||||
(br $loop)
|
||||
)
|
||||
)
|
||||
;; Store final count at ptr
|
||||
(i32.store (local.get $ptr) (local.get $i))
|
||||
)
|
||||
)"#;
|
||||
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();
|
||||
}
|
||||
|
||||
// 500 actors spawned, one message each, all process
|
||||
#[test]
|
||||
fn five_hundred_actors_one_message_each() {
|
||||
let engine = SharedEngine::new().unwrap();
|
||||
let rt = Runtime::new(RuntimeConfig::default());
|
||||
let inbox = rt.new_inbox::<ByteMessage>().unwrap();
|
||||
|
||||
for i in 0..500u16 {
|
||||
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.to_le_bytes())).unwrap();
|
||||
}
|
||||
for _ in 0..20 {
|
||||
rt.tick();
|
||||
}
|
||||
|
||||
let count = std::iter::from_fn(|| inbox.try_recv()).count();
|
||||
assert_eq!(count, 500, "all 500 actors echoed");
|
||||
}
|
||||
|
||||
// Property: echo payload is always byte-for-byte identical
|
||||
proptest! {
|
||||
#[test]
|
||||
fn prop_echo_bitwise_identical(
|
||||
payload in proptest::collection::vec(0u8..=255, 0..500)
|
||||
) {
|
||||
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();
|
||||
|
||||
rt.send_to(addr, framed_msg(inbox.addr(), &payload)).unwrap();
|
||||
rt.tick();
|
||||
|
||||
let resp = inbox.try_recv().expect("echo response");
|
||||
assert_eq!(resp.0, payload);
|
||||
}
|
||||
}
|
||||
|
||||
// Guest sends response with modified last byte (add 1 to last byte)
|
||||
#[test]
|
||||
fn guest_modifies_last_byte() {
|
||||
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_start i32)
|
||||
(local $payload_len i32)
|
||||
(local $last_idx i32)
|
||||
(if (i32.lt_u (local.get $len) (i32.const 33)) (then return))
|
||||
(local.set $payload_start (i32.add (local.get $ptr) (i32.const 32)))
|
||||
(local.set $payload_len (i32.sub (local.get $len) (i32.const 32)))
|
||||
;; Add 1 to last byte
|
||||
(local.set $last_idx
|
||||
(i32.add (local.get $payload_start)
|
||||
(i32.sub (local.get $payload_len) (i32.const 1))))
|
||||
(i32.store8 (local.get $last_idx)
|
||||
(i32.add (i32.load8_u (local.get $last_idx)) (i32.const 1)))
|
||||
(call $send (local.get $ptr) (local.get $payload_start) (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::<ByteMessage>().unwrap();
|
||||
let addr = rt.spawn(actor).unwrap();
|
||||
|
||||
rt.send_to(addr, framed_msg(inbox.addr(), &[0x10, 0x20, 0x30])).unwrap();
|
||||
rt.tick();
|
||||
|
||||
let resp = inbox.try_recv().expect("modified response");
|
||||
assert_eq!(resp.0, vec![0x10, 0x20, 0x31], "last byte incremented");
|
||||
}
|
||||
Loading…
Reference in a new issue