bin-runner #36
1 changed files with 151 additions and 0 deletions
|
|
@ -9937,4 +9937,155 @@ proptest! {
|
|||
rt.tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Cycle 68 — 300 TEST MILESTONE ────────────────────────────────────────────
|
||||
|
||||
// Guest that writes to memory offset 0 (data segment area) — valid operation
|
||||
#[test]
|
||||
fn guest_writes_to_offset_zero() {
|
||||
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)
|
||||
(i32.store (i32.const 0) (i32.const 42))
|
||||
)
|
||||
)"#;
|
||||
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![1])).unwrap();
|
||||
rt.tick();
|
||||
}
|
||||
|
||||
// Spawn 10 echo actors, send to all, verify all echo back
|
||||
#[test]
|
||||
fn ten_echo_actors_all_respond() {
|
||||
let engine = SharedEngine::new().unwrap();
|
||||
let rt = Runtime::new(RuntimeConfig::default());
|
||||
let inbox = rt.new_inbox::<ByteMessage>().unwrap();
|
||||
|
||||
let addrs: Vec<_> = (0..10)
|
||||
.map(|_| {
|
||||
let actor = WasmActorBuilder::new(engine.clone(), guest_wasm("echo"))
|
||||
.build().unwrap();
|
||||
rt.spawn(actor).unwrap()
|
||||
})
|
||||
.collect();
|
||||
|
||||
for (i, addr) in addrs.iter().enumerate() {
|
||||
rt.send_to(*addr, framed_msg(inbox.addr(), &[i as u8])).unwrap();
|
||||
}
|
||||
rt.tick();
|
||||
|
||||
let mut msgs: Vec<Vec<u8>> = std::iter::from_fn(|| inbox.try_recv().map(|m| m.0)).collect();
|
||||
msgs.sort();
|
||||
assert_eq!(msgs.len(), 10);
|
||||
for i in 0..10 {
|
||||
assert_eq!(msgs[i], vec![i as u8]);
|
||||
}
|
||||
}
|
||||
|
||||
// Guest that does nothing in handle then sends on next message
|
||||
#[test]
|
||||
fn guest_alternates_between_silent_and_sending() {
|
||||
let wat = r#"(module
|
||||
(import "swactor" "send" (func $send (param i32 i32 i32)))
|
||||
(memory (export "memory") 1)
|
||||
(global $toggle (mut i32) (i32.const 0))
|
||||
(func (export "alloc") (param $len i32) (result i32) i32.const 1024)
|
||||
(func (export "handle") (param $ptr i32) (param $len i32)
|
||||
(if (i32.eqz (global.get $toggle))
|
||||
(then
|
||||
;; Silent on even calls
|
||||
(global.set $toggle (i32.const 1))
|
||||
)
|
||||
(else
|
||||
;; Send on odd calls
|
||||
(global.set $toggle (i32.const 0))
|
||||
(if (i32.ge_u (local.get $len) (i32.const 33))
|
||||
(then
|
||||
(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();
|
||||
|
||||
// Send 6 messages — expect responses from messages 2, 4, 6 (1-indexed)
|
||||
for i in 0..6 {
|
||||
rt.send_to(addr, framed_msg(inbox.addr(), &[i])).unwrap();
|
||||
}
|
||||
rt.tick();
|
||||
|
||||
let msgs: Vec<Vec<u8>> = std::iter::from_fn(|| inbox.try_recv().map(|m| m.0)).collect();
|
||||
assert_eq!(msgs.len(), 3, "only odd calls send");
|
||||
assert_eq!(msgs[0], vec![1]);
|
||||
assert_eq!(msgs[1], vec![3]);
|
||||
assert_eq!(msgs[2], vec![5]);
|
||||
}
|
||||
|
||||
// Runtime with budget=1 processes exactly 1 message per actor per tick
|
||||
#[test]
|
||||
fn budget_one_processes_exactly_one_per_tick() {
|
||||
let engine = SharedEngine::new().unwrap();
|
||||
let actor = WasmActorBuilder::new(engine, guest_wasm("echo")).build().unwrap();
|
||||
let mut cfg = RuntimeConfig::default();
|
||||
cfg.actor_message_budget = 1;
|
||||
let rt = Runtime::new(cfg);
|
||||
let inbox = rt.new_inbox::<ByteMessage>().unwrap();
|
||||
let addr = rt.spawn(actor).unwrap();
|
||||
|
||||
// Queue 3 messages
|
||||
for i in 0..3u8 {
|
||||
rt.send_to(addr, framed_msg(inbox.addr(), &[i])).unwrap();
|
||||
}
|
||||
|
||||
// Each tick should process exactly 1
|
||||
rt.tick();
|
||||
let t1: Vec<_> = std::iter::from_fn(|| inbox.try_recv().map(|m| m.0)).collect();
|
||||
assert_eq!(t1.len(), 1, "budget=1 processes 1 per tick");
|
||||
assert_eq!(t1[0], vec![0]);
|
||||
|
||||
rt.tick();
|
||||
let t2: Vec<_> = std::iter::from_fn(|| inbox.try_recv().map(|m| m.0)).collect();
|
||||
assert_eq!(t2.len(), 1);
|
||||
assert_eq!(t2[0], vec![1]);
|
||||
|
||||
rt.tick();
|
||||
let t3: Vec<_> = std::iter::from_fn(|| inbox.try_recv().map(|m| m.0)).collect();
|
||||
assert_eq!(t3.len(), 1);
|
||||
assert_eq!(t3[0], vec![2]);
|
||||
}
|
||||
|
||||
// ByteMessage with exactly 32 bytes (address only, no payload for echo)
|
||||
#[test]
|
||||
fn echo_with_address_only_no_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();
|
||||
|
||||
// Exactly 32 bytes = dest address, 0 payload bytes
|
||||
let msg = ByteMessage(inbox.addr().0.to_vec());
|
||||
rt.send_to(addr, msg).unwrap();
|
||||
rt.tick();
|
||||
|
||||
// Echo should send back empty payload
|
||||
let resp = inbox.try_recv().expect("echo with 0-byte payload");
|
||||
assert!(resp.0.is_empty());
|
||||
}
|
||||
Loading…
Reference in a new issue