2026-03-24 13:40:42 +00:00

56 lines
2.0 KiB
Rust

use prometeu_bytecode::isa::core::{CoreOpCode, CoreOpCodeSpecExt};
use prometeu_vm::{VirtualMachine, BudgetReport, LogicalFrameEndingReason};
use prometeu_hal::{HostContext, HostReturn, NativeInterface, SyscallId};
#[test]
fn scheduler_wake_and_ready_order_is_deterministic() {
// Black-box determinism: run a tiny program twice and ensure it yields the same sequence
// of LogicalFrameEndingReason for identical budgets.
fn emit(op: CoreOpCode, imm: Option<&[u8]>, out: &mut Vec<u8>) {
out.extend_from_slice(&(op as u16).to_le_bytes());
let need = op.spec().imm_bytes as usize;
match (need, imm) {
(0, None) => {}
(n, Some(bytes)) if bytes.len() == n => out.extend_from_slice(bytes),
(n, Some(bytes)) => panic!("imm size mismatch for {:?}: need {}, got {}", op, n, bytes.len()),
(n, None) => panic!("missing imm for {:?}: need {} bytes", op, n),
}
}
struct NoopNative;
impl NativeInterface for NoopNative {
fn syscall(
&mut self,
_id: SyscallId,
_args: &[prometeu_bytecode::Value],
_ret: &mut HostReturn,
_ctx: &mut HostContext,
) -> Result<(), prometeu_hal::vm_fault::VmFault> {
Ok(())
}
}
// Program: FRAME_SYNC; HALT
let mut rom = Vec::new();
emit(CoreOpCode::FrameSync, None, &mut rom);
emit(CoreOpCode::Halt, None, &mut rom);
let run_once = || -> Vec<LogicalFrameEndingReason> {
let mut vm = VirtualMachine::new(rom.clone(), vec![]);
vm.prepare_call("0");
let mut native = NoopNative;
let mut ctx = HostContext::new(None);
let mut reasons = Vec::new();
let r1: BudgetReport = vm.run_budget(10, &mut native, &mut ctx).unwrap();
reasons.push(r1.reason);
let r2: BudgetReport = vm.run_budget(10, &mut native, &mut ctx).unwrap();
reasons.push(r2.reason);
reasons
};
let a = run_once();
let b = run_once();
assert_eq!(a, b, "execution reasons must be deterministic");
}