test: Cycle 51 — XOR transform, address reconstruction, double+watch, constructors
- guest_xor_transform_key: loop-based XOR cipher with 0xAA key - actor_address_reconstructible_from_bytes: ActorAddress round-trips through raw bytes - double_guest_watch_fires_once_on_stop: double works + watch notifies exactly once - byte_message_various_constructors: ByteMessage from string, empty, 1024 zeros All 220 tests pass. No new bugs found. Authored by Claude, lovingly guided by Zachery Aaron Shores-Chmielewski
This commit is contained in:
parent
f235266300
commit
372c0bbf9e
1 changed files with 125 additions and 0 deletions
|
|
@ -7643,4 +7643,129 @@ proptest! {
|
||||||
|
|
||||||
while let Some(_) = inbox.try_recv() {}
|
while let Some(_) = inbox.try_recv() {}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Guest with i32.xor for byte transformation ─────────────────────────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn guest_xor_transform_key() {
|
||||||
|
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)
|
||||||
|
(local $i i32)
|
||||||
|
(local $payload_start i32)
|
||||||
|
(local $payload_len i32)
|
||||||
|
(local.set $payload_start (i32.add (local.get $ptr) (i32.const 32)))
|
||||||
|
(local.set $payload_len (i32.sub (local.get $len) (i32.const 32)))
|
||||||
|
(local.set $i (i32.const 0))
|
||||||
|
(block $break
|
||||||
|
(loop $loop
|
||||||
|
(br_if $break (i32.ge_u (local.get $i) (local.get $payload_len)))
|
||||||
|
(i32.store8
|
||||||
|
(i32.add (i32.const 200) (local.get $i))
|
||||||
|
(i32.xor
|
||||||
|
(i32.load8_u (i32.add (local.get $payload_start) (local.get $i)))
|
||||||
|
(i32.const 0xAA)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
||||||
|
(br $loop)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(call $send (local.get $ptr) (i32.const 200) (local.get $payload_len))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
"#;
|
||||||
|
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(), &[0x00, 0xFF, 0x55, 0xAA])).unwrap();
|
||||||
|
rt.tick();
|
||||||
|
|
||||||
|
let msg = inbox.try_recv().unwrap();
|
||||||
|
assert_eq!(msg.0, vec![0xAA, 0x55, 0xFF, 0x00], "XOR with 0xAA");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── ActorAddress reconstruction from raw bytes ──────────────────────────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn actor_address_reconstructible_from_bytes() {
|
||||||
|
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();
|
||||||
|
|
||||||
|
let reconstructed = ActorAddress(addr.0);
|
||||||
|
assert_eq!(addr, reconstructed);
|
||||||
|
|
||||||
|
rt.send_to(reconstructed, framed_msg(inbox.addr(), b"reconstructed")).unwrap();
|
||||||
|
rt.tick();
|
||||||
|
let msg = inbox.try_recv().unwrap();
|
||||||
|
assert_eq!(msg.0, b"reconstructed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Double guest + watch fires once on stop ─────────────────────────────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn double_guest_watch_fires_once_on_stop() {
|
||||||
|
let engine = SharedEngine::new().unwrap();
|
||||||
|
let actor = WasmActorBuilder::new(engine, guest_wasm("double")).build().unwrap();
|
||||||
|
|
||||||
|
let exit_count = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
|
||||||
|
let ec = exit_count.clone();
|
||||||
|
|
||||||
|
struct WatchCounter3 {
|
||||||
|
target: Option<ActorAddress>,
|
||||||
|
count: std::sync::Arc<std::sync::atomic::AtomicUsize>,
|
||||||
|
}
|
||||||
|
impl ActorInterface for WatchCounter3 {
|
||||||
|
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 double_addr = rt.spawn(actor).unwrap();
|
||||||
|
let w_addr = rt.spawn(WatchCounter3 { target: Some(double_addr), count: ec }).unwrap();
|
||||||
|
|
||||||
|
rt.send_to(w_addr, ByteMessage(vec![])).unwrap();
|
||||||
|
rt.tick();
|
||||||
|
|
||||||
|
rt.send_to(double_addr, framed_msg(inbox.addr(), b"D")).unwrap();
|
||||||
|
rt.tick();
|
||||||
|
assert_eq!(inbox.try_recv().unwrap().0, b"D");
|
||||||
|
assert_eq!(inbox.try_recv().unwrap().0, b"D");
|
||||||
|
|
||||||
|
rt.stop_actor(double_addr);
|
||||||
|
rt.tick();
|
||||||
|
rt.tick();
|
||||||
|
assert_eq!(exit_count.load(std::sync::atomic::Ordering::SeqCst), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── ByteMessage constructors ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn byte_message_various_constructors() {
|
||||||
|
let msg1 = ByteMessage("hello".as_bytes().to_vec());
|
||||||
|
assert_eq!(msg1.0, b"hello");
|
||||||
|
let msg2 = ByteMessage(Vec::new());
|
||||||
|
assert!(msg2.0.is_empty());
|
||||||
|
let msg3 = ByteMessage(vec![0; 1024]);
|
||||||
|
assert_eq!(msg3.0.len(), 1024);
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue