bin-runner #36
1 changed files with 142 additions and 1 deletions
|
|
@ -4367,7 +4367,7 @@ fn pingpong_wasm_echoes_bounded_by_budget() {
|
|||
let rt = Runtime::new(config);
|
||||
let inbox = rt.new_inbox::<ByteMessage>().unwrap();
|
||||
let a1 = rt.spawn(echo1).unwrap();
|
||||
let a2 = rt.spawn(echo2).unwrap();
|
||||
let _a2 = rt.spawn(echo2).unwrap();
|
||||
|
||||
// Seed: tell actor 1 to echo to actor 2, with addr of actor 1 as payload
|
||||
// so actor 2's reply goes back to actor 1 (creating a loop).
|
||||
|
|
@ -4626,3 +4626,144 @@ fn send_dest_one_past_memory_boundary_traps() {
|
|||
// This time no OOB send, but the module always tries the OOB send, so still trapped
|
||||
assert!(inbox.try_recv().is_none(), "same module always traps");
|
||||
}
|
||||
|
||||
// ── Reference types module rejected by engine ───────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn reference_types_module_rejected() {
|
||||
// Module uses externref (reference types disabled in SharedEngine).
|
||||
let wat = r#"
|
||||
(module
|
||||
(memory (export "memory") 1)
|
||||
(func (export "alloc") (param i32) (result i32) i32.const 0)
|
||||
(func (export "handle") (param i32 i32))
|
||||
(table 1 externref)
|
||||
)
|
||||
"#;
|
||||
let wasm = wat::parse_str(wat).unwrap();
|
||||
let engine = SharedEngine::new().unwrap();
|
||||
let result = WasmActorBuilder::new(engine, wasm).build();
|
||||
assert!(result.is_err(), "reference types should be rejected by sandboxed engine");
|
||||
}
|
||||
|
||||
// ── Two actors, one traps always, one works — independent store isolation ───
|
||||
|
||||
#[test]
|
||||
fn trapping_actor_does_not_affect_sibling() {
|
||||
// Actor 1 always traps in handle. Actor 2 echos normally.
|
||||
// Verify trap in actor 1 doesn't corrupt/poison actor 2.
|
||||
let trap_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 i32 i32)
|
||||
unreachable
|
||||
)
|
||||
)
|
||||
"#;
|
||||
let trap_wasm = wat::parse_str(trap_wat).unwrap();
|
||||
let engine = SharedEngine::new().unwrap();
|
||||
let trapper = WasmActorBuilder::new(engine.clone(), trap_wasm).build().unwrap();
|
||||
let echoer = WasmActorBuilder::new(engine, guest_wasm("echo")).build().unwrap();
|
||||
|
||||
let rt = Runtime::new(RuntimeConfig::default());
|
||||
let inbox = rt.new_inbox::<ByteMessage>().unwrap();
|
||||
let trap_addr = rt.spawn(trapper).unwrap();
|
||||
let echo_addr = rt.spawn(echoer).unwrap();
|
||||
|
||||
// Send to both in the same tick
|
||||
rt.send_to(trap_addr, framed_msg(inbox.addr(), b"trap-this")).unwrap();
|
||||
rt.send_to(echo_addr, framed_msg(inbox.addr(), b"echo-this")).unwrap();
|
||||
rt.tick();
|
||||
|
||||
// Only echo actor should deliver
|
||||
let mut msgs = Vec::new();
|
||||
while let Some(msg) = inbox.try_recv() {
|
||||
msgs.push(msg.0);
|
||||
}
|
||||
assert_eq!(msgs.len(), 1, "only echo actor should deliver");
|
||||
assert_eq!(&msgs[0], b"echo-this");
|
||||
}
|
||||
|
||||
// ── Alloc returns negative for non-zero len — message dropped gracefully ────
|
||||
|
||||
#[test]
|
||||
fn alloc_returns_negative_for_nonzero_drops_message() {
|
||||
// Guest alloc always returns -42 regardless of input.
|
||||
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 -42)
|
||||
(func (export "handle") (param i32 i32)
|
||||
;; Should never be called because alloc returns negative
|
||||
unreachable
|
||||
)
|
||||
)
|
||||
"#;
|
||||
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();
|
||||
|
||||
// Send several messages — all should be silently dropped
|
||||
for i in 0..5 {
|
||||
rt.send_to(addr, framed_msg(inbox.addr(), &[i])).unwrap();
|
||||
}
|
||||
rt.tick();
|
||||
|
||||
assert!(inbox.try_recv().is_none(), "negative alloc should drop messages");
|
||||
|
||||
// Actor should still be alive — send another message, still dropped
|
||||
rt.send_to(addr, framed_msg(inbox.addr(), &[99])).unwrap();
|
||||
rt.tick();
|
||||
assert!(inbox.try_recv().is_none(), "actor alive but still drops (negative alloc)");
|
||||
}
|
||||
|
||||
// ── WASM actor with global state accumulates across messages ────────────────
|
||||
|
||||
#[test]
|
||||
fn global_counter_accumulates_across_messages() {
|
||||
// Guest has a mutable global counter. Each handle call increments it.
|
||||
// The response payload includes the counter value.
|
||||
let wat = r#"
|
||||
(module
|
||||
(import "swactor" "send" (func $send (param i32 i32 i32)))
|
||||
(memory (export "memory") 1)
|
||||
(global $counter (mut i32) (i32.const 0))
|
||||
|
||||
(func (export "alloc") (param i32) (result i32) i32.const 4096)
|
||||
(func (export "handle") (param $ptr i32) (param $len i32)
|
||||
;; Increment counter
|
||||
(global.set $counter (i32.add (global.get $counter) (i32.const 1)))
|
||||
;; Write counter value as single byte at offset 200
|
||||
(i32.store8 (i32.const 200) (global.get $counter))
|
||||
;; Send counter value back to sender (first 32 bytes of msg)
|
||||
(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();
|
||||
|
||||
// Send 5 messages across 5 ticks
|
||||
for _ in 0..5 {
|
||||
rt.send_to(addr, framed_msg(inbox.addr(), b"inc")).unwrap();
|
||||
rt.tick();
|
||||
}
|
||||
|
||||
let mut counter_values = Vec::new();
|
||||
while let Some(msg) = inbox.try_recv() {
|
||||
counter_values.push(msg.0[0]);
|
||||
}
|
||||
assert_eq!(counter_values, vec![1, 2, 3, 4, 5], "global state should persist across handle calls");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue