test: Cycle 42 — internal calls, raw WAT, build-discard, float ops, empty bytes

- module_with_internal_function_calls: helper functions (double, add_ten) composed in handle
- build_from_raw_wat_bytes: WAT-compiled silent actor works
- build_and_discard_100_actors: WasmActor drops cleanly 100 times
- handle_uses_floating_point: f64 arithmetic with i32.trunc_f64_s
- empty_wasm_bytes_error: empty Vec<u8> produces build error

All 180 tests pass. No new bugs found.

Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
This commit is contained in:
Claude 2026-02-13 09:50:34 +00:00
parent 8e8ea693a6
commit c8bf78231b

View file

@ -6400,3 +6400,138 @@ fn fan_in_from_multiple_native_senders() {
assert_eq!(msg.0, vec![i as u8], "inbox {i} should get correct payload"); assert_eq!(msg.0, vec![i as u8], "inbox {i} should get correct payload");
} }
} }
// ── Module with multiple functions calling each other ────────────────────────
#[test]
fn module_with_internal_function_calls() {
// Guest has helper functions called from handle.
let wat = r#"
(module
(import "swactor" "send" (func $send (param i32 i32 i32)))
(memory (export "memory") 1)
(func $double (param $x i32) (result i32)
(i32.mul (local.get $x) (i32.const 2))
)
(func $add_ten (param $x i32) (result i32)
(i32.add (local.get $x) (i32.const 10))
)
(func (export "alloc") (param i32) (result i32) i32.const 4096)
(func (export "handle") (param $ptr i32) (param $len i32)
;; Read first payload byte, double it, add 10
(i32.store8 (i32.const 200)
(call $add_ten
(call $double
(i32.load8_u (i32.add (local.get $ptr) (i32.const 32)))
)
)
)
(call $send (local.get $ptr) (i32.const 200) (i32.const 1))
)
)
"#;
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();
// input=5: double(5)=10, add_ten(10)=20
rt.send_to(addr, framed_msg(inbox.addr(), &[5])).unwrap();
// input=100: double(100)=200, add_ten(200)=210
rt.send_to(addr, framed_msg(inbox.addr(), &[100])).unwrap();
rt.tick();
let msg1 = inbox.try_recv().unwrap();
let msg2 = inbox.try_recv().unwrap();
assert_eq!(msg1.0[0], 20);
assert_eq!(msg2.0[0], 210);
}
// ── Build from pre-compiled WAT bytes (no guest dir needed) ─────────────────
#[test]
fn build_from_raw_wat_bytes() {
let wat = r#"
(module
(memory (export "memory") 1)
(func (export "alloc") (param i32) (result i32) i32.const 0)
(func (export "handle") (param i32 i32))
)
"#;
// Convert WAT → WASM at runtime
let wasm = wat::parse_str(wat).unwrap();
let engine = SharedEngine::new().unwrap();
let actor = WasmActorBuilder::new(engine, wasm).build().unwrap();
// Verify it works (silent actor — no sends)
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"silent")).unwrap();
rt.tick();
assert!(inbox.try_recv().is_none(), "silent actor should not send anything");
}
// ── Rapidly build and discard actors without spawning ────────────────────────
#[test]
fn build_and_discard_100_actors() {
// Build 100 actors but don't spawn them. Tests that WasmActor drops cleanly.
let engine = SharedEngine::new().unwrap();
for _ in 0..100 {
let actor = WasmActorBuilder::new(engine.clone(), guest_wasm("echo"))
.build()
.unwrap();
drop(actor);
}
// No panic = success
}
// ── Guest with f32/f64 floating point ops ───────────────────────────────────
#[test]
fn handle_uses_floating_point() {
// Guest performs f64 arithmetic and stores result as i32.
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)
;; Compute floor(3.14 * 10) = floor(31.4) = 31
(i32.store8 (i32.const 200)
(i32.trunc_f64_s
(f64.mul (f64.const 3.14) (f64.const 10.0))
)
)
(call $send (local.get $ptr) (i32.const 200) (i32.const 1))
)
)
"#;
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"float")).unwrap();
rt.tick();
let msg = inbox.try_recv().unwrap();
assert_eq!(msg.0[0], 31, "floor(3.14 * 10) = 31");
}
// ── Empty WASM bytes produces error ─────────────────────────────────────────
#[test]
fn empty_wasm_bytes_error() {
let engine = SharedEngine::new().unwrap();
let result = WasmActorBuilder::new(engine, Vec::<u8>::new()).build();
assert!(result.is_err(), "empty bytes should fail");
}