bin-runner #36
1 changed files with 135 additions and 0 deletions
|
|
@ -8678,4 +8678,139 @@ fn builder_accepts_slice_reference() {
|
|||
let addr = rt.spawn(actor).unwrap();
|
||||
rt.send_to(addr, ByteMessage(vec![])).unwrap();
|
||||
rt.tick();
|
||||
}
|
||||
|
||||
// ── Cycle 59 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
// Guest that uses local.tee instruction (sets local and leaves value on stack)
|
||||
#[test]
|
||||
fn guest_uses_local_tee() {
|
||||
let wat = r#"(module
|
||||
(memory (export "memory") 1)
|
||||
(global $heap (mut i32) (i32.const 65536))
|
||||
(func (export "alloc") (param $len i32) (result i32)
|
||||
global.get $heap
|
||||
global.get $heap
|
||||
local.get $len
|
||||
i32.add
|
||||
global.set $heap)
|
||||
(func (export "handle") (param $ptr i32) (param $len i32)
|
||||
;; Use local.tee: ptr2 = alloc_start, also keep on stack
|
||||
(local $ptr2 i32)
|
||||
global.get $heap
|
||||
local.tee $ptr2
|
||||
drop ;; just exercising the instruction
|
||||
)
|
||||
)"#;
|
||||
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(b"tee".to_vec())).unwrap();
|
||||
rt.tick(); // no trap
|
||||
}
|
||||
|
||||
// Guest that writes a 16KB response — tests larger-than-page payloads round-trip
|
||||
#[test]
|
||||
fn sixteen_kb_payload_round_trip() {
|
||||
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..16384u32).map(|i| (i % 251) as u8).collect();
|
||||
rt.send_to(addr, framed_msg(inbox.addr(), &payload)).unwrap();
|
||||
rt.tick();
|
||||
|
||||
let resp = inbox.try_recv().expect("should receive 16KB echo");
|
||||
assert_eq!(resp.0, payload);
|
||||
}
|
||||
|
||||
// Two runtimes with WASM actors operating independently at the same time
|
||||
#[test]
|
||||
fn two_independent_runtimes_with_wasm_actors() {
|
||||
let engine = SharedEngine::new().unwrap();
|
||||
let actor1 = WasmActorBuilder::new(engine.clone(), guest_wasm("echo")).build().unwrap();
|
||||
let actor2 = WasmActorBuilder::new(engine, guest_wasm("double")).build().unwrap();
|
||||
|
||||
let rt1 = Runtime::new(RuntimeConfig::default());
|
||||
let rt2 = Runtime::new(RuntimeConfig::default());
|
||||
|
||||
let inbox1 = rt1.new_inbox::<ByteMessage>().unwrap();
|
||||
let inbox2 = rt2.new_inbox::<ByteMessage>().unwrap();
|
||||
|
||||
let addr1 = rt1.spawn(actor1).unwrap();
|
||||
let addr2 = rt2.spawn(actor2).unwrap();
|
||||
|
||||
rt1.send_to(addr1, framed_msg(inbox1.addr(), b"A")).unwrap();
|
||||
rt2.send_to(addr2, framed_msg(inbox2.addr(), b"B")).unwrap();
|
||||
|
||||
rt1.tick();
|
||||
rt2.tick();
|
||||
|
||||
let msgs1: Vec<_> = std::iter::from_fn(|| inbox1.try_recv().map(|m| m.0)).collect();
|
||||
let msgs2: Vec<_> = std::iter::from_fn(|| inbox2.try_recv().map(|m| m.0)).collect();
|
||||
|
||||
assert_eq!(msgs1.len(), 1, "echo from rt1");
|
||||
assert_eq!(msgs2.len(), 2, "double from rt2");
|
||||
assert_eq!(msgs1[0], b"A");
|
||||
assert!(msgs2.iter().all(|m| m == b"B"));
|
||||
}
|
||||
|
||||
// Guest that uses i32.wrap_i64 instruction
|
||||
#[test]
|
||||
fn guest_uses_i64_to_i32_wrap() {
|
||||
let wat = r#"(module
|
||||
(memory (export "memory") 1)
|
||||
(global $heap (mut i32) (i32.const 65536))
|
||||
(func (export "alloc") (param $len i32) (result i32)
|
||||
global.get $heap
|
||||
global.get $heap
|
||||
local.get $len
|
||||
i32.add
|
||||
global.set $heap)
|
||||
(func (export "handle") (param $ptr i32) (param $len i32)
|
||||
;; Wrap i64 to i32: 0x1_0000_00FF -> 0xFF
|
||||
i64.const 4294967551 ;; 0x1_000000FF
|
||||
i32.wrap_i64
|
||||
;; result is 255, store at ptr
|
||||
local.get $ptr
|
||||
i32.store8 offset=0
|
||||
)
|
||||
)"#;
|
||||
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(b"W".to_vec())).unwrap();
|
||||
rt.tick(); // no trap, wrapping is well-defined
|
||||
}
|
||||
|
||||
// Property test: building from random subsets of valid WAT always succeeds or fails cleanly
|
||||
proptest! {
|
||||
#[test]
|
||||
fn prop_random_valid_wat_variations_never_panic(
|
||||
alloc_offset in 1024u32..60000,
|
||||
pages in 1u32..5,
|
||||
handle_body in proptest::bool::ANY,
|
||||
) {
|
||||
let store_body = if handle_body {
|
||||
"local.get $ptr\nlocal.get $ptr\ni32.load8_u\ni32.store8"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let wat = format!(r#"(module
|
||||
(memory (export "memory") {pages})
|
||||
(func (export "alloc") (param $len i32) (result i32)
|
||||
i32.const {alloc_offset})
|
||||
(func (export "handle") (param $ptr i32) (param $len i32)
|
||||
{store_body})
|
||||
)"#);
|
||||
let engine = SharedEngine::new().unwrap();
|
||||
let result = WasmActorBuilder::new(engine, wat::parse_str(&wat).unwrap()).build();
|
||||
assert!(result.is_ok(), "valid WAT should build");
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue