diff --git a/crates/wasm-actor/tests/wasm_actor.rs b/crates/wasm-actor/tests/wasm_actor.rs index 524e418..ad5eaea 100644 --- a/crates/wasm-actor/tests/wasm_actor.rs +++ b/crates/wasm-actor/tests/wasm_actor.rs @@ -10088,4 +10088,127 @@ fn echo_with_address_only_no_payload() { // Echo should send back empty payload let resp = inbox.try_recv().expect("echo with 0-byte payload"); assert!(resp.0.is_empty()); +} + +// ── Cycle 69 ───────────────────────────────────────────────────────────────── + +// Guest with multiple globals — verifies complex state management +#[test] +fn guest_with_multiple_mutable_globals() { + let wat = r#"(module + (memory (export "memory") 1) + (global $a (mut i32) (i32.const 0)) + (global $b (mut i32) (i32.const 100)) + (global $c (mut i32) (i32.const 200)) + (func (export "alloc") (param $len i32) (result i32) i32.const 1024) + (func (export "handle") (param $ptr i32) (param $len i32) + ;; Rotate: a=b, b=c, c=a+1 + (local $old_a i32) + (local.set $old_a (global.get $a)) + (global.set $a (global.get $b)) + (global.set $b (global.get $c)) + (global.set $c (i32.add (local.get $old_a) (i32.const 1))) + ) + )"#; + 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(); + + // Send 10 messages — complex state rotation + for _ in 0..10 { + rt.send_to(addr, ByteMessage(vec![0])).unwrap(); + } + rt.tick(); // no trap +} + +// Builder with very large WASM module (100KB of data segment) +#[test] +fn large_data_segment_module() { + // Module with a large data segment (4KB of zeros) + let mut wat = String::from(r#"(module + (memory (export "memory") 2) + (data (i32.const 0) ""#); + // Add 4096 escaped null bytes + for _ in 0..4096 { + wat.push_str("\\00"); + } + wat.push_str(r#"") + (func (export "alloc") (param $len i32) (result i32) i32.const 65536) + (func (export "handle") (param $ptr i32) (param $len i32)) + )"#); + 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![1; 100])).unwrap(); + rt.tick(); +} + +// Echo then double from same engine in same tick — interleaved processing +#[test] +fn echo_and_double_interleaved_in_same_tick() { + let engine = SharedEngine::new().unwrap(); + let echo = WasmActorBuilder::new(engine.clone(), guest_wasm("echo")).build().unwrap(); + let double = WasmActorBuilder::new(engine, guest_wasm("double")).build().unwrap(); + let rt = Runtime::new(RuntimeConfig::default()); + let inbox = rt.new_inbox::().unwrap(); + + let e_addr = rt.spawn(echo).unwrap(); + let d_addr = rt.spawn(double).unwrap(); + + // Alternate: echo, double, echo, double + for i in 0..4u8 { + let addr = if i % 2 == 0 { e_addr } else { d_addr }; + rt.send_to(addr, framed_msg(inbox.addr(), &[i])).unwrap(); + } + rt.tick(); + + let msgs: Vec> = std::iter::from_fn(|| inbox.try_recv().map(|m| m.0)).collect(); + // Echo: 2 msgs (1 response each) + Double: 2 msgs (2 responses each) = 6 + assert_eq!(msgs.len(), 6); +} + +// Guest that stores i64 value (8-byte store) +#[test] +fn guest_stores_i64_value() { + 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) + ;; Store 8-byte i64 at ptr (if len >= 8) + (if (i32.ge_u (local.get $len) (i32.const 8)) + (then + (i64.store (local.get $ptr) (i64.const 0x0102030405060708)) + ) + ) + ) + )"#; + 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; 8])).unwrap(); + rt.tick(); +} + +// Runtime dropped while actors are processing — no crash +#[test] +fn runtime_dropped_with_active_wasm_actors() { + let engine = SharedEngine::new().unwrap(); + { + let rt = Runtime::new(RuntimeConfig::default()); + for _ in 0..5 { + let actor = WasmActorBuilder::new(engine.clone(), guest_wasm("echo")) + .build().unwrap(); + let addr = rt.spawn(actor).unwrap(); + rt.send_to(addr, ByteMessage(vec![1, 2, 3])).unwrap(); + } + rt.tick(); + // rt dropped here with live actors and unprocessed responses + } + // No panic — wasmtime Store cleanup is safe } \ No newline at end of file