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 82ca6869aa - Show all commits

View file

@ -6673,3 +6673,136 @@ fn wasm_actor_error_is_send_and_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<WasmActorError>();
}
// ── Message with all 256 byte values round-trips correctly ──────────────────
#[test]
fn all_256_byte_values_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();
// Send all 256 byte values as payload
let payload: Vec<u8> = (0..=255).collect();
rt.send_to(addr, framed_msg(inbox.addr(), &payload)).unwrap();
rt.tick();
let msg = inbox.try_recv().unwrap();
assert_eq!(msg.0.len(), 256, "should receive all 256 bytes");
for (i, &byte) in msg.0.iter().enumerate() {
assert_eq!(byte, i as u8, "byte {i} should be {i}");
}
}
// ── Guest uses memory.fill to initialize a region ───────────────────────────
#[test]
fn guest_uses_memory_fill_for_response() {
// Guest fills a region with a constant byte and sends it.
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 4096)
(func (export "handle") (param $ptr i32) (param $len i32)
;; Fill 10 bytes at offset 200 with value 0x42 ('B')
(memory.fill (i32.const 200) (i32.const 0x42) (i32.const 10))
(call $send (local.get $ptr) (i32.const 200) (i32.const 10))
)
)
"#;
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"fill")).unwrap();
rt.tick();
let msg = inbox.try_recv().unwrap();
assert_eq!(msg.0, vec![0x42; 10], "should receive 10 'B' bytes");
}
// ── WASM actor processes messages after watcher is installed ─────────────────
#[test]
fn wasm_actor_works_normally_while_being_watched() {
let engine = SharedEngine::new().unwrap();
let actor = WasmActorBuilder::new(engine, guest_wasm("echo")).build().unwrap();
let exit_count = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
let exit_clone = exit_count.clone();
struct WatchAndCount2 {
target: Option<ActorAddress>,
count: std::sync::Arc<std::sync::atomic::AtomicUsize>,
}
impl ActorInterface for WatchAndCount2 {
type Incoming = ByteMessage;
type Response = ();
fn handle(&mut self, ctx: &Ctx, _msg: ByteMessage) {
if let Some(t) = self.target.take() {
ctx.watch(t);
}
}
fn on_actor_exit(&mut self, _ctx: &Ctx, _exited: swactor::actor::ActorExited) {
self.count.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
}
}
let rt = Runtime::new(RuntimeConfig::default());
let inbox = rt.new_inbox::<ByteMessage>().unwrap();
let wasm_addr = rt.spawn(actor).unwrap();
let watcher = WatchAndCount2 { target: Some(wasm_addr), count: exit_clone };
let watcher_addr = rt.spawn(watcher).unwrap();
// Install the watch
rt.send_to(watcher_addr, ByteMessage(vec![])).unwrap();
rt.tick();
// WASM actor should still work normally
for i in 0u8..5 {
rt.send_to(wasm_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, vec![0, 1, 2, 3, 4], "actor should work normally while watched");
// Stop it — watcher should be notified
rt.stop_actor(wasm_addr);
rt.tick();
rt.tick();
assert_eq!(exit_count.load(std::sync::atomic::Ordering::SeqCst), 1);
}
// ── Double guest processes 0xFF payload (boundary byte value) ───────────────
#[test]
fn double_guest_with_max_byte_value() {
let engine = SharedEngine::new().unwrap();
let actor = WasmActorBuilder::new(engine, guest_wasm("double")).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(), &[0xFF])).unwrap();
rt.tick();
let msg1 = inbox.try_recv().expect("first double");
let msg2 = inbox.try_recv().expect("second double");
assert_eq!(msg1.0, vec![0xFF]);
assert_eq!(msg2.0, vec![0xFF]);
assert!(inbox.try_recv().is_none(), "exactly two copies");
}