add unit tests

This commit is contained in:
Nilton Constantino 2026-01-15 15:45:16 +00:00
parent ba57c96d6e
commit 0b78bf4797
No known key found for this signature in database
3 changed files with 138 additions and 0 deletions

View File

@ -130,4 +130,28 @@ impl OpCode {
OpCode::FrameSync => 1, OpCode::FrameSync => 1,
} }
} }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_opcode_decoding() {
assert_eq!(OpCode::try_from(0x00).unwrap(), OpCode::Nop);
assert_eq!(OpCode::try_from(0x10).unwrap(), OpCode::PushConst);
assert_eq!(OpCode::try_from(0x20).unwrap(), OpCode::Add);
assert_eq!(OpCode::try_from(0x70).unwrap(), OpCode::Syscall);
assert_eq!(OpCode::try_from(0x80).unwrap(), OpCode::FrameSync);
assert!(OpCode::try_from(0xFFFF).is_err());
}
#[test]
fn test_opcode_cycles() {
assert_eq!(OpCode::Nop.cycles(), 1);
assert_eq!(OpCode::Add.cycles(), 2);
assert_eq!(OpCode::Mul.cycles(), 4);
assert_eq!(OpCode::Div.cycles(), 6);
assert_eq!(OpCode::Alloc.cycles(), 10);
}
} }

View File

@ -40,4 +40,38 @@ impl Value {
_ => None, _ => None,
} }
} }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_value_equality() {
assert_eq!(Value::Integer(10), Value::Integer(10));
assert_eq!(Value::Float(10.5), Value::Float(10.5));
assert_eq!(Value::Integer(10), Value::Float(10.0));
assert_eq!(Value::Float(10.0), Value::Integer(10));
assert_ne!(Value::Integer(10), Value::Integer(11));
assert_ne!(Value::Integer(10), Value::Float(10.1));
assert_eq!(Value::Boolean(true), Value::Boolean(true));
assert_ne!(Value::Boolean(true), Value::Boolean(false));
assert_eq!(Value::String("oi".into()), Value::String("oi".into()));
assert_eq!(Value::Null, Value::Null);
}
#[test]
fn test_value_conversions() {
let v_int = Value::Integer(42);
assert_eq!(v_int.as_float(), Some(42.0));
assert_eq!(v_int.as_integer(), Some(42));
let v_float = Value::Float(42.7);
assert_eq!(v_float.as_float(), Some(42.7));
assert_eq!(v_float.as_integer(), Some(42));
let v_bool = Value::Boolean(true);
assert_eq!(v_bool.as_float(), None);
assert_eq!(v_bool.as_integer(), None);
}
} }

View File

@ -634,4 +634,84 @@ mod tests {
assert_eq!(machine.vm.operand_stack.last(), Some(&Value::Integer(42))); assert_eq!(machine.vm.operand_stack.last(), Some(&Value::Integer(42)));
assert!(machine.vm.halted); assert!(machine.vm.halted);
} }
#[test]
fn test_stack_operations() {
struct NoopNative;
impl NativeInterface for NoopNative {
fn syscall(&mut self, _id: u32, _vm: &mut VirtualMachine) -> Result<u64, String> { Ok(0) }
}
let mut native = NoopNative;
let mut rom = Vec::new();
// PUSH 10, PUSH 20, SWAP, POP
rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes());
rom.extend_from_slice(&0u32.to_le_bytes());
rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes());
rom.extend_from_slice(&1u32.to_le_bytes());
rom.extend_from_slice(&(OpCode::Swap as u16).to_le_bytes());
rom.extend_from_slice(&(OpCode::Pop as u16).to_le_bytes());
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
let constant_pool = vec![Value::Integer(10), Value::Integer(20)];
let mut vm = VirtualMachine::new(rom, constant_pool);
vm.run_budget(100, &mut native).unwrap();
assert_eq!(vm.operand_stack.len(), 1);
assert_eq!(vm.operand_stack[0], Value::Integer(20));
}
#[test]
fn test_logical_operations() {
struct NoopNative;
impl NativeInterface for NoopNative {
fn syscall(&mut self, _id: u32, _vm: &mut VirtualMachine) -> Result<u64, String> { Ok(0) }
}
let mut native = NoopNative;
let mut rom = Vec::new();
// PUSH true, NOT (-> false), PUSH true, OR (-> true)
rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes());
rom.extend_from_slice(&0u32.to_le_bytes()); // true
rom.extend_from_slice(&(OpCode::Not as u16).to_le_bytes());
rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes());
rom.extend_from_slice(&0u32.to_le_bytes()); // true
rom.extend_from_slice(&(OpCode::Or as u16).to_le_bytes());
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
let constant_pool = vec![Value::Boolean(true)];
let mut vm = VirtualMachine::new(rom, constant_pool);
vm.run_budget(100, &mut native).unwrap();
assert_eq!(vm.operand_stack.last(), Some(&Value::Boolean(true)));
}
#[test]
fn test_scopes() {
struct NoopNative;
impl NativeInterface for NoopNative {
fn syscall(&mut self, _id: u32, _vm: &mut VirtualMachine) -> Result<u64, String> { Ok(0) }
}
let mut native = NoopNative;
let mut rom = Vec::new();
// PUSH_SCOPE 2 (reserves 2 nulls), PUSH 42, SET_LOCAL 0, GET_LOCAL 0, POP_SCOPE
rom.extend_from_slice(&(OpCode::PushScope as u16).to_le_bytes());
rom.extend_from_slice(&2u32.to_le_bytes());
rom.extend_from_slice(&(OpCode::PushConst as u16).to_le_bytes());
rom.extend_from_slice(&0u32.to_le_bytes()); // 42
rom.extend_from_slice(&(OpCode::SetLocal as u16).to_le_bytes());
rom.extend_from_slice(&0u32.to_le_bytes());
rom.extend_from_slice(&(OpCode::GetLocal as u16).to_le_bytes());
rom.extend_from_slice(&0u32.to_le_bytes());
rom.extend_from_slice(&(OpCode::PopScope as u16).to_le_bytes());
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
let constant_pool = vec![Value::Integer(42)];
let mut vm = VirtualMachine::new(rom, constant_pool);
vm.run_budget(100, &mut native).unwrap();
// Stack should be empty because POP_SCOPE truncates to stack_base
assert_eq!(vm.operand_stack.len(), 0);
}
} }