test: Cycle 27 — mixed outbox partial delivery, drop-oldest mailbox, full inbox drop
- mixed_outbox_partial_delivery: guest sends to valid + garbage addresses in same handle, verifies only valid sends deliver and actor survives - drop_oldest_mailbox_with_wasm_actor: DropOldest policy keeps newest 3 of 5 messages - echo_to_full_inbox_silently_drops: bounded inbox capacity silently drops excess echoes All 119 tests pass. No new bugs found. Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
This commit is contained in:
parent
57ff6f1d81
commit
3f9d27e1bd
1 changed files with 131 additions and 0 deletions
|
|
@ -4213,3 +4213,134 @@ fn double_guest_with_minimal_payload() {
|
|||
assert!(second.0.is_empty(), "payload should be empty");
|
||||
assert!(inbox.try_recv().is_none(), "exactly two messages");
|
||||
}
|
||||
|
||||
// ── Mixed outbox: some sends succeed, some fail ─────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn mixed_outbox_partial_delivery() {
|
||||
// Guest sends to a valid address (inbox) and an invalid address (garbage)
|
||||
// in the same handle call. The valid send should deliver; the invalid one
|
||||
// should silently fail. The actor should survive.
|
||||
let wat = r#"
|
||||
(module
|
||||
(import "swactor" "send" (func $send (param i32 i32 i32)))
|
||||
(memory (export "memory") 1)
|
||||
;; Garbage address at offset 500 (all 0xDE bytes)
|
||||
(data (i32.const 500) "\de\de\de\de\de\de\de\de\de\de\de\de\de\de\de\de\de\de\de\de\de\de\de\de\de\de\de\de\de\de\de\de")
|
||||
|
||||
(func (export "alloc") (param i32) (result i32) i32.const 4096)
|
||||
(func (export "handle") (param $ptr i32) (param $len i32)
|
||||
;; Send #1: to valid address (from message bytes)
|
||||
(i32.store8 (i32.const 200) (i32.const 65)) ;; 'A'
|
||||
local.get $ptr
|
||||
i32.const 200
|
||||
i32.const 1
|
||||
call $send
|
||||
|
||||
;; Send #2: to garbage address at offset 500
|
||||
(i32.store8 (i32.const 201) (i32.const 66)) ;; 'B'
|
||||
i32.const 500 ;; garbage dest
|
||||
i32.const 201
|
||||
i32.const 1
|
||||
call $send
|
||||
|
||||
;; Send #3: back to valid address
|
||||
(i32.store8 (i32.const 202) (i32.const 67)) ;; 'C'
|
||||
local.get $ptr
|
||||
i32.const 202
|
||||
i32.const 1
|
||||
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();
|
||||
|
||||
rt.send_to(addr, framed_msg(inbox.addr(), b"mixed-outbox")).unwrap();
|
||||
rt.tick();
|
||||
|
||||
// Should receive sends #1 and #3 (valid dest), but not #2 (garbage dest)
|
||||
let mut received = Vec::new();
|
||||
while let Some(msg) = inbox.try_recv() {
|
||||
received.push(msg.0[0]);
|
||||
}
|
||||
assert_eq!(received, vec![b'A', b'C'], "only valid-address sends should deliver");
|
||||
}
|
||||
|
||||
// ── Drop-oldest mailbox policy with WASM actor ─────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn drop_oldest_mailbox_with_wasm_actor() {
|
||||
use swactor::runtime::MailboxOverflow;
|
||||
|
||||
let engine = SharedEngine::new().unwrap();
|
||||
let actor = WasmActorBuilder::new(engine, guest_wasm("echo"))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let config = RuntimeConfig {
|
||||
default_mailbox_capacity: 3,
|
||||
mailbox_overflow: MailboxOverflow::DropOldest,
|
||||
..RuntimeConfig::default()
|
||||
};
|
||||
let rt = Runtime::new(config);
|
||||
let inbox = rt.new_inbox::<ByteMessage>().unwrap();
|
||||
let addr = rt.spawn(actor).unwrap();
|
||||
|
||||
// Send 5 messages before tick — DropOldest keeps the last 3
|
||||
for i in 0u8..5 {
|
||||
rt.send_to(addr, framed_msg(inbox.addr(), &[i])).unwrap();
|
||||
}
|
||||
rt.tick();
|
||||
|
||||
let mut received = Vec::new();
|
||||
while let Some(msg) = inbox.try_recv() {
|
||||
received.push(msg.0[0]);
|
||||
}
|
||||
|
||||
assert_eq!(received.len(), 3, "should keep 3 messages");
|
||||
// DropOldest keeps the newest: [2, 3, 4]
|
||||
assert_eq!(received, vec![2, 3, 4], "DropOldest should keep newest messages");
|
||||
}
|
||||
|
||||
// ── WASM actor echoes to inbox, inbox full — message dropped ────────────────
|
||||
|
||||
#[test]
|
||||
fn echo_to_full_inbox_silently_drops() {
|
||||
// Echo sends to an inbox that has a bounded capacity.
|
||||
// If the inbox is full, the send should silently fail.
|
||||
use swactor::runtime::MailboxOverflow;
|
||||
|
||||
let engine = SharedEngine::new().unwrap();
|
||||
let actor = WasmActorBuilder::new(engine, guest_wasm("echo"))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let config = RuntimeConfig {
|
||||
default_mailbox_capacity: 2,
|
||||
mailbox_overflow: MailboxOverflow::DropNewest,
|
||||
..RuntimeConfig::default()
|
||||
};
|
||||
let rt = Runtime::new(config);
|
||||
let inbox = rt.new_inbox::<ByteMessage>().unwrap();
|
||||
let addr = rt.spawn(actor).unwrap();
|
||||
|
||||
// Send 5 messages — actor has capacity 2, so only first 2 are kept
|
||||
// Each message echoes to inbox (also capacity 2)
|
||||
for i in 0u8..5 {
|
||||
rt.send_to(addr, framed_msg(inbox.addr(), &[i])).unwrap();
|
||||
}
|
||||
rt.tick();
|
||||
|
||||
// Inbox has capacity 2, so at most 2 messages received
|
||||
let mut received = Vec::new();
|
||||
while let Some(msg) = inbox.try_recv() {
|
||||
received.push(msg.0[0]);
|
||||
}
|
||||
assert!(received.len() <= 2, "inbox should be bounded to capacity 2");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue