use rulefire::vm::bytecode::{Instruction, Opcode, Program, BYTECODE_MAGIC};
#[test]
fn bytecode_magic_is_correct() {
assert_eq!(&BYTECODE_MAGIC, b"YBC0");
}
#[test]
fn instruction_new_creates_correct_opcode() {
let inst = Instruction::new(Opcode::PushTrue, 42);
assert_eq!(inst.opcode, 1);
assert_eq!(inst.operand, 42);
}
#[test]
fn opcode_from_u32_valid_values() {
assert_eq!(Opcode::from_u32(1).unwrap(), Opcode::PushTrue);
assert_eq!(Opcode::from_u32(2).unwrap(), Opcode::PushFalse);
assert_eq!(Opcode::from_u32(30).unwrap(), Opcode::Halt);
}
#[test]
fn opcode_from_u32_invalid_value() {
let result = Opcode::from_u32(0);
assert!(result.is_err());
let result = Opcode::from_u32(50);
assert!(result.is_err());
let result = Opcode::from_u32(999);
assert!(result.is_err());
}
#[test]
fn program_to_bytes_empty() {
let program = Program {
instructions: vec![Instruction::new(Opcode::Halt, 0)],
};
let bytes = program.to_bytes();
assert!(bytes.len() >= 8); assert_eq!(&bytes[..4], BYTECODE_MAGIC);
}
#[test]
fn program_roundtrip_single_instruction() {
let program = Program {
instructions: vec![
Instruction::new(Opcode::PushTrue, 0),
Instruction::new(Opcode::Halt, 0),
],
};
let bytes = program.to_bytes();
let roundtrip = Program::from_bytes(&bytes).unwrap();
assert_eq!(roundtrip.instructions, program.instructions);
}
#[test]
fn program_roundtrip_all_opcodes() {
let program = Program {
instructions: vec![
Instruction::new(Opcode::PushTrue, 0),
Instruction::new(Opcode::PushFalse, 0),
Instruction::new(Opcode::PushStringMatched, 1),
Instruction::new(Opcode::PushStringCount, 2),
Instruction::new(Opcode::PushStringOffset, 3),
Instruction::new(Opcode::PushStringLength, 4),
Instruction::new(Opcode::PushFileSize, 0),
Instruction::new(Opcode::PushEntryCount, 0),
Instruction::new(Opcode::PushImmediate, 9),
Instruction::new(Opcode::PushNumStrings, 0),
Instruction::new(Opcode::And, 0),
Instruction::new(Opcode::Or, 0),
Instruction::new(Opcode::Not, 0),
Instruction::new(Opcode::Eq, 0),
Instruction::new(Opcode::Neq, 0),
Instruction::new(Opcode::Lt, 0),
Instruction::new(Opcode::Gt, 0),
Instruction::new(Opcode::Lte, 0),
Instruction::new(Opcode::Gte, 0),
Instruction::new(Opcode::Add, 0),
Instruction::new(Opcode::Sub, 0),
Instruction::new(Opcode::CountOf, (2 << 16) | 1),
Instruction::new(Opcode::AllOf, 2),
Instruction::new(Opcode::AnyOf, 2),
Instruction::new(Opcode::StringAt, 1),
Instruction::new(Opcode::StringIn, 1),
Instruction::new(Opcode::ForAny, 1 << 16),
Instruction::new(Opcode::EndFor, 0),
Instruction::new(Opcode::ForAll, 1 << 16),
Instruction::new(Opcode::EndFor, 0),
Instruction::new(Opcode::Halt, 0),
],
};
let bytes = program.to_bytes();
let roundtrip = Program::from_bytes(&bytes).unwrap();
assert_eq!(roundtrip.instructions, program.instructions);
}
#[test]
fn program_from_bytes_missing_magic() {
let bytes = b"XXXX";
let result = Program::from_bytes(bytes);
assert!(result.is_err());
}
#[test]
fn program_from_bytes_truncated() {
let mut bytes = BYTECODE_MAGIC.to_vec();
bytes.extend_from_slice(&1u32.to_le_bytes()); let result = Program::from_bytes(&bytes);
assert!(result.is_err());
}
#[test]
fn program_from_bytes_size_mismatch() {
let mut bytes = BYTECODE_MAGIC.to_vec();
bytes.extend_from_slice(&2u32.to_le_bytes()); bytes.extend_from_slice(&1u32.to_le_bytes()); bytes.extend_from_slice(&0u32.to_le_bytes()); let result = Program::from_bytes(&bytes);
assert!(result.is_err());
}
#[test]
fn program_validate_empty() {
let program = Program {
instructions: vec![],
};
let result = program.validate();
assert!(result.is_err());
}
#[test]
fn program_validate_no_halt() {
let program = Program {
instructions: vec![Instruction::new(Opcode::PushTrue, 0)],
};
let result = program.validate();
assert!(result.is_err());
}
#[test]
fn program_validate_halt_not_last() {
let program = Program {
instructions: vec![
Instruction::new(Opcode::Halt, 0),
Instruction::new(Opcode::PushTrue, 0),
],
};
let result = program.validate();
assert!(result.is_err());
}
#[test]
fn program_validate_valid() {
let program = Program {
instructions: vec![
Instruction::new(Opcode::PushTrue, 0),
Instruction::new(Opcode::Halt, 0),
],
};
let result = program.validate();
assert!(result.is_ok());
}
#[test]
fn program_validate_nested_loops_ok() {
let program = Program {
instructions: vec![
Instruction::new(Opcode::ForAny, 4 << 16),
Instruction::new(Opcode::ForAll, 2 << 16),
Instruction::new(Opcode::PushTrue, 0),
Instruction::new(Opcode::EndFor, 0),
Instruction::new(Opcode::EndFor, 0),
Instruction::new(Opcode::Halt, 0),
],
};
let result = program.validate();
assert!(result.is_ok());
}
#[test]
fn program_validate_unmatched_for_any() {
let program = Program {
instructions: vec![
Instruction::new(Opcode::ForAny, 1 << 16),
Instruction::new(Opcode::PushTrue, 0),
Instruction::new(Opcode::Halt, 0),
],
};
let result = program.validate();
assert!(result.is_err());
}
#[test]
fn program_validate_unmatched_endfor() {
let program = Program {
instructions: vec![
Instruction::new(Opcode::EndFor, 0),
Instruction::new(Opcode::Halt, 0),
],
};
let result = program.validate();
assert!(result.is_err());
}
#[test]
fn instruction_kind_returns_correct_opcode() {
let inst = Instruction::new(Opcode::PushTrue, 0);
assert_eq!(inst.kind().unwrap(), Opcode::PushTrue);
let inst = Instruction::new(Opcode::Halt, 0);
assert_eq!(inst.kind().unwrap(), Opcode::Halt);
}
#[test]
fn instruction_kind_invalid_opcode() {
let inst = Instruction {
opcode: 999,
operand: 0,
};
assert!(inst.kind().is_err());
}
#[test]
fn program_with_large_operand_roundtrips() {
let program = Program {
instructions: vec![
Instruction::new(Opcode::PushImmediate, u32::MAX),
Instruction::new(Opcode::Halt, 0),
],
};
let bytes = program.to_bytes();
let roundtrip = Program::from_bytes(&bytes).unwrap();
assert_eq!(roundtrip.instructions[0].operand, u32::MAX);
}