bin-runner #36

Merged
zacheryasc merged 103 commits from bin-runner into master 2026-02-13 14:11:40 +00:00
Showing only changes of commit 21c6f58907 - Show all commits

View file

@ -11854,4 +11854,138 @@ fn echo_single_byte_payload_integrity() {
for (i, &b) in responses.iter().enumerate() {
assert_eq!(b, i as u8);
}
}
// ── Cycle 83 ─────────────────────────────────────────────────────────────────
// Spawn echo, send 1000 messages, verify all received over multiple ticks
#[test]
fn thousand_message_echo_stress() {
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();
for i in 0u16..1000 {
rt.send_to(addr, framed_msg(inbox.addr(), &i.to_le_bytes())).unwrap();
}
for _ in 0..50 {
rt.tick();
}
let count = std::iter::from_fn(|| inbox.try_recv()).count();
assert_eq!(count, 1000, "all 1000 messages echoed");
}
// Guest with type annotations on all locals (verbose WAT)
#[test]
fn guest_with_many_typed_locals() {
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)
(local $a i32) (local $b i32) (local $c i32)
(local $d i64) (local $e f32) (local $f f64)
(local.set $a (local.get $len))
(local.set $b (i32.mul (local.get $a) (i32.const 2)))
(local.set $c (i32.add (local.get $a) (local.get $b)))
(local.set $d (i64.extend_i32_u (local.get $c)))
(local.set $e (f32.convert_i32_s (local.get $c)))
(local.set $f (f64.promote_f32 (local.get $e)))
(i32.store (local.get $ptr) (local.get $c))
)
)"#;
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![0u8; 4])).unwrap();
rt.tick();
}
// Same actor handles empty and non-empty messages alternately
#[test]
fn alternate_empty_and_nonempty_messages() {
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();
for i in 0..10 {
if i % 2 == 0 {
rt.send_to(addr, ByteMessage(vec![])).unwrap(); // empty
} else {
rt.send_to(addr, framed_msg(inbox.addr(), &[i as u8])).unwrap();
}
}
rt.tick();
// Only the non-empty framed messages should produce echo responses
let msgs: Vec<Vec<u8>> = std::iter::from_fn(|| inbox.try_recv().map(|m| m.0)).collect();
assert_eq!(msgs.len(), 5, "5 framed messages echoed");
}
// WasmActorError variants have distinct Display representations
#[test]
fn error_variants_have_distinct_display() {
let missing = WasmActorError::MissingExport("memory");
let wasmtime_err = WasmActorError::Wasmtime(wasmtime::Error::msg("test error"));
let s1 = format!("{missing}");
let s2 = format!("{wasmtime_err}");
assert_ne!(s1, s2, "error variants should have different display");
assert!(s1.contains("memory"));
assert!(s2.contains("test error"));
}
// Two different WAT modules on same runtime — heterogeneous actors
#[test]
fn heterogeneous_wat_actors_on_same_runtime() {
// Actor 1: echoes
let wat_echo = r#"(module
(import "swactor" "send" (func $send (param i32 i32 i32)))
(memory (export "memory") 1)
(func (export "alloc") (param $len i32) (result i32) i32.const 1024)
(func (export "handle") (param $ptr i32) (param $len i32)
(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))))))
)"#;
// Actor 2: always sends byte 0x42
let wat_const = r#"(module
(import "swactor" "send" (func $send (param i32 i32 i32)))
(memory (export "memory") 1)
(data (i32.const 900) "\42")
(func (export "alloc") (param $len i32) (result i32) i32.const 1024)
(func (export "handle") (param $ptr i32) (param $len i32)
(if (i32.ge_u (local.get $len) (i32.const 32))
(then (call $send (local.get $ptr) (i32.const 900) (i32.const 1)))))
)"#;
let engine = SharedEngine::new().unwrap();
let a1 = WasmActorBuilder::new(engine.clone(), wat::parse_str(wat_echo).unwrap())
.build().unwrap();
let a2 = WasmActorBuilder::new(engine, wat::parse_str(wat_const).unwrap())
.build().unwrap();
let rt = Runtime::new(RuntimeConfig::default());
let inbox = rt.new_inbox::<ByteMessage>().unwrap();
let addr1 = rt.spawn(a1).unwrap();
let addr2 = rt.spawn(a2).unwrap();
rt.send_to(addr1, framed_msg(inbox.addr(), b"hello")).unwrap();
let mut msg2 = Vec::new();
msg2.extend_from_slice(&inbox.addr().0);
rt.send_to(addr2, ByteMessage(msg2)).unwrap();
rt.tick();
let msgs: Vec<Vec<u8>> = std::iter::from_fn(|| inbox.try_recv().map(|m| m.0)).collect();
assert_eq!(msgs.len(), 2);
// One is "hello", other is [0x42]
assert!(msgs.iter().any(|m| m == b"hello"));
assert!(msgs.iter().any(|m| m == &[0x42]));
}