use std::fs; use std::path::Path; use prometeu_abi::virtual_machine::{Value, VmFault}; use prometeu_hardware::hardware::Hardware; use prometeu_hardware_contract::{HostContext, HostReturn, NativeInterface}; use prometeu_vm::{LogicalFrameEndingReason, VirtualMachine}; struct MockNative; impl NativeInterface for MockNative { fn syscall(&mut self, id: u32, _args: &[Value], ret: &mut HostReturn, _ctx: &mut HostContext) -> Result<(), VmFault> { if id == 0x2010 { // InputPadSnapshot for _ in 0..48 { ret.push_bool(false); } } else if id == 0x2011 { // InputTouchSnapshot for _ in 0..6 { ret.push_int(0); } } else { // Push one result for others that might expect it // Based on results_count() in syscalls.rs, most return 1 except GfxClear565 (0) if id != 0x1010 { ret.push_null(); } } Ok(()) } } #[test] fn test_canonical_cartridge_heartbeat() { let mut pbc_path = Path::new("../../test-cartridges/canonical/golden/program.pbc").to_path_buf(); if !pbc_path.exists() { pbc_path = Path::new("test-cartridges/canonical/golden/program.pbc").to_path_buf(); } let pbc_bytes = fs::read(pbc_path).expect("Failed to read canonical PBC. Did you run the generation test?"); // Determine entrypoint from the compiled module exports let entry_symbol = "src/main/modules:frame"; let mut vm = VirtualMachine::new(vec![], vec![]); vm.initialize(pbc_bytes, entry_symbol).expect("Failed to initialize VM with canonical cartridge"); vm.prepare_call(entry_symbol); let mut native = MockNative; let mut hw = Hardware::new(); let mut ctx = HostContext::new(Some(&mut hw)); // Run for a reasonable budget let report = vm.run_budget(1000, &mut native, &mut ctx).expect("VM failed to run"); // Acceptance criteria: // 1. No traps match report.reason { LogicalFrameEndingReason::Trap(trap) => panic!("VM trapped: {:?}", trap), LogicalFrameEndingReason::Panic(msg) => panic!("VM panicked: {}", msg), LogicalFrameEndingReason::Halted => {}, LogicalFrameEndingReason::EndOfRom => {}, LogicalFrameEndingReason::FrameSync => {}, LogicalFrameEndingReason::BudgetExhausted => {}, LogicalFrameEndingReason::Breakpoint => {}, } // 2. Deterministic output state (if any) // In our frame(), z should be 30. // Local 2 in frame() should be 30. // Let's check the stack or locals if possible. // The VM should have finished 'frame'. // Since 'frame' returns void, the stack should be empty (or have the return value if any, but it's void). assert_eq!(vm.operand_stack.len(), 0, "Stack should be empty after frame() execution"); println!("Heartbeat test passed!"); }