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
|
|
@ -7644,3 +7644,128 @@ proptest! {
|
|||
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