test: Cycle 44 — all 256 bytes, memory.fill, watched actor, double 0xFF
- all_256_byte_values_round_trip: every byte value 0x00-0xFF echoes correctly - guest_uses_memory_fill_for_response: memory.fill writes 10 constant bytes - wasm_actor_works_normally_while_being_watched: watched actor processes 5 msgs then stops - double_guest_with_max_byte_value: double guest handles 0xFF payload correctly All 188 tests pass. No new bugs found. Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
This commit is contained in:
parent
a6fea775ac
commit
82ca6869aa
1 changed files with 133 additions and 0 deletions
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue