bin-runner #36
1 changed files with 138 additions and 0 deletions
|
|
@ -1064,6 +1064,144 @@ fn overlapping_dest_and_payload_in_send_works() {
|
|||
assert_eq!(received.0.len(), 32, "payload should be 32 bytes from overlapping region");
|
||||
}
|
||||
|
||||
// ── Type mismatch: non-ByteMessage sent to WASM actor ────────────────────────
|
||||
|
||||
#[test]
|
||||
fn non_byte_message_to_wasm_actor_is_silently_ignored() {
|
||||
// Sending a message of the wrong type (not ByteMessage) to a WASM actor.
|
||||
// The runtime's handle_any downcast fails, counting a type mismatch.
|
||||
// The actor should survive and still process valid ByteMessages.
|
||||
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 wrong type — u32 instead of ByteMessage
|
||||
// This goes through send_any with Box::new(42u32), downcast to ByteMessage fails.
|
||||
rt.send_to(addr, 42u32).unwrap();
|
||||
rt.tick(); // type mismatch — silently ignored
|
||||
|
||||
// Actor still alive — send a valid message
|
||||
let payload = b"after mismatch";
|
||||
rt.send_to(addr, framed_msg(inbox.addr(), payload)).unwrap();
|
||||
rt.tick();
|
||||
|
||||
let received = inbox.try_recv().expect("actor should work after type mismatch");
|
||||
assert_eq!(received.0, payload);
|
||||
}
|
||||
|
||||
// ── Guest state persistence: mutable global survives across messages ─────────
|
||||
|
||||
#[test]
|
||||
fn guest_mutable_state_persists_across_messages() {
|
||||
// A guest module with a mutable global counter. Each handle call increments
|
||||
// the counter and includes it in the reply payload. Verifies that the
|
||||
// wasmtime Store and linear memory persist between handle() calls.
|
||||
let wat = r#"
|
||||
(module
|
||||
(import "swactor" "send" (func $send (param i32 i32 i32)))
|
||||
(memory (export "memory") 1)
|
||||
(global $counter (mut i32) (i32.const 0))
|
||||
|
||||
(func (export "alloc") (param i32) (result i32)
|
||||
i32.const 256
|
||||
)
|
||||
(func (export "handle") (param $ptr i32) (param $len i32)
|
||||
;; Increment counter
|
||||
global.get $counter
|
||||
i32.const 1
|
||||
i32.add
|
||||
global.set $counter
|
||||
|
||||
;; Write counter value to memory at offset 200
|
||||
(i32.store8 (i32.const 200) (global.get $counter))
|
||||
|
||||
;; Send counter byte as payload to dest at $ptr
|
||||
local.get $ptr ;; dest_ptr (first 32 bytes of message)
|
||||
i32.const 200 ;; payload_ptr (counter byte)
|
||||
i32.const 1 ;; payload_len
|
||||
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();
|
||||
|
||||
// Send 3 messages, each should get an incrementing counter
|
||||
for expected in 1..=3u8 {
|
||||
rt.send_to(addr, framed_msg(inbox.addr(), b"x")).unwrap();
|
||||
rt.tick();
|
||||
let received = inbox.try_recv().expect("should receive counter reply");
|
||||
assert_eq!(received.0, vec![expected], "counter should increment per message");
|
||||
}
|
||||
}
|
||||
|
||||
// ── Spawn WASM from handler: native actor spawns WASM actor during handle ────
|
||||
|
||||
struct WasmSpawner {
|
||||
engine: SharedEngine,
|
||||
wasm_bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct SpawnAndForward {
|
||||
inbox_addr: ActorAddress,
|
||||
payload: Vec<u8>,
|
||||
}
|
||||
|
||||
impl ActorInterface for WasmSpawner {
|
||||
type Incoming = SpawnAndForward;
|
||||
type Response = ();
|
||||
fn handle(&mut self, ctx: &Ctx, msg: SpawnAndForward) {
|
||||
let actor = WasmActorBuilder::new(self.engine.clone(), self.wasm_bytes.clone())
|
||||
.build()
|
||||
.unwrap();
|
||||
let wasm_addr = ctx.spawn(actor);
|
||||
let _ = ctx.send(wasm_addr.unwrap(), framed_msg(&msg.inbox_addr, &msg.payload));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_handler_spawns_wasm_actor_and_forwards_message() {
|
||||
let engine = SharedEngine::new().unwrap();
|
||||
let wasm_bytes = guest_wasm("echo");
|
||||
|
||||
let spawner = WasmSpawner {
|
||||
engine: engine.clone(),
|
||||
wasm_bytes: wasm_bytes.clone(),
|
||||
};
|
||||
|
||||
let rt = Runtime::new(RuntimeConfig::default());
|
||||
let inbox = rt.new_inbox::<ByteMessage>().unwrap();
|
||||
let spawner_addr = rt.spawn(spawner).unwrap();
|
||||
|
||||
rt.send_to(
|
||||
spawner_addr,
|
||||
SpawnAndForward {
|
||||
inbox_addr: *inbox.addr(),
|
||||
payload: b"spawned-echo".to_vec(),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Tick 1: Spawner receives message, spawns WASM actor, sends to it
|
||||
rt.tick();
|
||||
// Tick 2: WASM actor processes message and echoes to inbox
|
||||
rt.tick();
|
||||
|
||||
let received = inbox.try_recv().expect("dynamically spawned WASM actor should echo");
|
||||
assert_eq!(received.0, b"spawned-echo");
|
||||
}
|
||||
|
||||
// ── Multi-worker: WASM actors across threads ─────────────────────────────────
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Reference in a new issue