test: Cycle 23 — conditional send, multi-page memory, 500-message sustained load
Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
This commit is contained in:
parent
c7f8a54b8a
commit
56bb9085d3
1 changed files with 133 additions and 0 deletions
|
|
@ -3779,3 +3779,136 @@ fn build_many_actors_from_same_bytes_sequentially() {
|
||||||
rt.tick();
|
rt.tick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Guest that only sends on even-numbered messages ─────────────────────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn guest_conditional_send_based_on_message_content() {
|
||||||
|
// Guest only sends a reply if the first byte of payload (after the 32-byte
|
||||||
|
// address) is even. Tests that the outbox is correctly empty when the guest
|
||||||
|
// decides not to send.
|
||||||
|
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 256)
|
||||||
|
(func (export "handle") (param $ptr i32) (param $len i32)
|
||||||
|
;; Check if byte at ptr+32 (first payload byte) is even
|
||||||
|
local.get $ptr
|
||||||
|
i32.const 32
|
||||||
|
i32.add
|
||||||
|
i32.load8_u
|
||||||
|
i32.const 2
|
||||||
|
i32.rem_u
|
||||||
|
i32.const 0
|
||||||
|
i32.eq
|
||||||
|
if
|
||||||
|
;; Even: send reply
|
||||||
|
local.get $ptr
|
||||||
|
local.get $ptr
|
||||||
|
i32.const 32
|
||||||
|
i32.add
|
||||||
|
local.get $len
|
||||||
|
i32.const 32
|
||||||
|
i32.sub
|
||||||
|
call $send
|
||||||
|
end
|
||||||
|
;; Odd: do nothing (empty outbox)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
"#;
|
||||||
|
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 even byte (0) — should get reply
|
||||||
|
rt.send_to(addr, framed_msg(inbox.addr(), &[0])).unwrap();
|
||||||
|
rt.tick();
|
||||||
|
assert!(inbox.try_recv().is_some(), "even byte should trigger reply");
|
||||||
|
|
||||||
|
// Send odd byte (1) — no reply
|
||||||
|
rt.send_to(addr, framed_msg(inbox.addr(), &[1])).unwrap();
|
||||||
|
rt.tick();
|
||||||
|
assert!(inbox.try_recv().is_none(), "odd byte should not trigger reply");
|
||||||
|
|
||||||
|
// Send even byte (2) — should get reply
|
||||||
|
rt.send_to(addr, framed_msg(inbox.addr(), &[2])).unwrap();
|
||||||
|
rt.tick();
|
||||||
|
assert!(inbox.try_recv().is_some(), "even byte should trigger reply");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Guest with multiple memory pages ────────────────────────────────────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn guest_with_multiple_initial_pages_works() {
|
||||||
|
// Module starts with 4 pages (256KiB). Alloc returns pointer in page 3.
|
||||||
|
let wat = r#"
|
||||||
|
(module
|
||||||
|
(import "swactor" "send" (func $send (param i32 i32 i32)))
|
||||||
|
(memory (export "memory") 4) ;; 4 pages = 262144 bytes
|
||||||
|
(func (export "alloc") (param i32) (result i32)
|
||||||
|
i32.const 196608 ;; page 3 start (3 * 65536)
|
||||||
|
)
|
||||||
|
(func (export "handle") (param $ptr i32) (param $len i32)
|
||||||
|
;; Echo from page 3
|
||||||
|
local.get $ptr
|
||||||
|
local.get $ptr
|
||||||
|
i32.const 32
|
||||||
|
i32.add
|
||||||
|
local.get $len
|
||||||
|
i32.const 32
|
||||||
|
i32.sub
|
||||||
|
call $send
|
||||||
|
)
|
||||||
|
)
|
||||||
|
"#;
|
||||||
|
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();
|
||||||
|
|
||||||
|
let payload = b"multi-page";
|
||||||
|
rt.send_to(addr, framed_msg(inbox.addr(), payload)).unwrap();
|
||||||
|
rt.tick();
|
||||||
|
|
||||||
|
let received = inbox.try_recv().expect("multi-page alloc should work");
|
||||||
|
assert_eq!(received.0, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Long-running: echo actor processes 500 messages sequentially ────────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn echo_processes_500_sequential_messages() {
|
||||||
|
// Sustained message processing without crashes, allocator exhaustion
|
||||||
|
// handling, and verified actor survival throughout.
|
||||||
|
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 mut received_count = 0;
|
||||||
|
for i in 0u16..500 {
|
||||||
|
let payload = i.to_le_bytes();
|
||||||
|
rt.send_to(addr, framed_msg(inbox.addr(), &payload)).unwrap();
|
||||||
|
rt.tick();
|
||||||
|
if let Some(msg) = inbox.try_recv() {
|
||||||
|
assert_eq!(msg.0, payload, "payload integrity at message {i}");
|
||||||
|
received_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// With 65536-byte allocator and ~40 bytes per alloc (34 + alignment),
|
||||||
|
// all 500 messages should fit. Verify all echoed correctly.
|
||||||
|
assert_eq!(received_count, 500, "all 500 messages should echo");
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue