diff --git a/crates/wasm-actor/tests/wasm_actor.rs b/crates/wasm-actor/tests/wasm_actor.rs index 6564db0..c4c07a4 100644 --- a/crates/wasm-actor/tests/wasm_actor.rs +++ b/crates/wasm-actor/tests/wasm_actor.rs @@ -5255,3 +5255,129 @@ fn send_payload_at_memory_offset_zero() { let msg = inbox.try_recv().expect("send from offset 0 should work"); assert_eq!(msg.0, b"OK"); } + +// ── WasmActorError From conversion ───────────────────────── + +#[test] +fn wasmtime_error_converts_to_wasm_actor_error() { + // Force a wasmtime::Error through the build path and verify the + // From conversion produces WasmActorError::Wasmtime variant. + let garbage = vec![0x00, 0x61, 0x73, 0x6D]; // valid magic but truncated + let engine = SharedEngine::new().unwrap(); + let result = WasmActorBuilder::new(engine, garbage).build(); + assert!(result.is_err()); + match result.err().unwrap() { + WasmActorError::Wasmtime(e) => { + // wasmtime::Error should have a non-empty message + let msg = format!("{e}"); + assert!(!msg.is_empty(), "wasmtime error should have a message"); + } + other => panic!("expected Wasmtime variant, got {other}"), + } +} + +// ── 3 WASM actors on 2-thread runtime all deliver ─────────────────────────── + +#[test] +fn three_wasm_actors_on_two_threads() { + use std::sync::atomic::{AtomicUsize, Ordering}; + use std::sync::Arc; + + let engine = SharedEngine::new().unwrap(); + let config = RuntimeConfig { + num_threads: 2, + ..RuntimeConfig::default() + }; + let rt = Runtime::new(config); + let counter = Arc::new(AtomicUsize::new(0)); + + // Spawn 3 echo actors + let mut addrs = Vec::new(); + for _ in 0..3 { + let actor = WasmActorBuilder::new(engine.clone(), guest_wasm("echo")) + .build() + .unwrap(); + addrs.push(rt.spawn(actor).unwrap()); + } + + // Create a native counter actor that counts received messages + struct MsgCounter(Arc); + impl ActorInterface for MsgCounter { + type Incoming = ByteMessage; + type Response = (); + fn handle(&mut self, _ctx: &Ctx, _msg: ByteMessage) { + self.0.fetch_add(1, Ordering::SeqCst); + } + } + + let counter_clone = counter.clone(); + let counter_addr = rt.spawn(MsgCounter(counter_clone)).unwrap(); + + // Send one message to each WASM actor, echoing to the counter + for addr in &addrs { + rt.send_to(*addr, framed_msg(&counter_addr, b"ping")).unwrap(); + } + + let handle = rt.run().unwrap(); + + // Poll for all 3 messages + let mut success = false; + for _ in 0..40 { + std::thread::sleep(std::time::Duration::from_millis(25)); + if counter.load(Ordering::SeqCst) >= 3 { + success = true; + break; + } + } + handle.shutdown(); + assert!(success, "all 3 WASM actors should deliver on 2-thread runtime"); +} + +// ── Module with if/else branching in handle ───────────────────────────────── + +#[test] +fn handle_with_if_else_branching() { + // Guest uses if/else to send different payloads based on message length. + 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) + ;; If payload (len - 32) > 5, send "BIG", else send "SML" + (if (i32.gt_s (i32.sub (local.get $len) (i32.const 32)) (i32.const 5)) + (then + (i32.store8 (i32.const 200) (i32.const 66)) ;; 'B' + (i32.store8 (i32.const 201) (i32.const 73)) ;; 'I' + (i32.store8 (i32.const 202) (i32.const 71)) ;; 'G' + (call $send (local.get $ptr) (i32.const 200) (i32.const 3)) + ) + (else + (i32.store8 (i32.const 200) (i32.const 83)) ;; 'S' + (i32.store8 (i32.const 201) (i32.const 77)) ;; 'M' + (i32.store8 (i32.const 202) (i32.const 76)) ;; 'L' + (call $send (local.get $ptr) (i32.const 200) (i32.const 3)) + ) + ) + ) + ) + "#; + 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::().unwrap(); + let addr = rt.spawn(actor).unwrap(); + + // Short payload (3 bytes <= 5) + rt.send_to(addr, framed_msg(inbox.addr(), b"abc")).unwrap(); + // Long payload (10 bytes > 5) + rt.send_to(addr, framed_msg(inbox.addr(), b"1234567890")).unwrap(); + rt.tick(); + + let msg1 = inbox.try_recv().expect("short message should get response"); + let msg2 = inbox.try_recv().expect("long message should get response"); + assert_eq!(msg1.0, b"SML"); + assert_eq!(msg2.0, b"BIG"); +}