add unit tests
This commit is contained in:
parent
ba57c96d6e
commit
0b78bf4797
@ -130,4 +130,28 @@ impl OpCode {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -40,4 +40,38 @@ impl Value {
|
||||
_ => 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);
|
||||
}
|
||||
}
|
||||
@ -634,4 +634,84 @@ mod tests {
|
||||
assert_eq!(machine.vm.operand_stack.last(), Some(&Value::Integer(42)));
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user