import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))

from obsidian256 import obsidianvm


def test_vm_registers():
    vm = obsidianvm()
    
    for i in range(16):
        vm.set_register(i, i * 10)
    
    for i in range(16):
        assert vm.get_register(i) == i * 10


def test_vm_memory():
    vm = obsidianvm()
    
    data = bytes(range(256))
    vm.load_data(data, 0x100)
    
    retrieved = vm.get_data(0x100, 256)
    assert retrieved == data


def test_vm_xor():
    vm = obsidianvm()
    vm.set_register(0, 0xaa)
    vm.set_register(1, 0x55)
    
    program = bytes([
        obsidianvm.op_xor, 0, 1,
        obsidianvm.op_halt,
    ])
    
    vm.load_program(program)
    vm.run()
    
    assert vm.get_register(0) == 0xff


def test_vm_add():
    vm = obsidianvm()
    vm.set_register(0, 100)
    vm.set_register(1, 50)
    
    program = bytes([
        obsidianvm.op_add, 0, 1,
        obsidianvm.op_halt,
    ])
    
    vm.load_program(program)
    vm.run()
    
    assert vm.get_register(0) == 150


def test_vm_rotation():
    vm = obsidianvm()
    vm.set_register(0, 0x80000000)
    
    program = bytes([
        obsidianvm.op_rol, 0, 1,
        obsidianvm.op_halt,
    ])
    
    vm.load_program(program)
    vm.run()
    
    assert vm.get_register(0) == 0x01


def test_vm_sbox():
    vm = obsidianvm()
    vm.set_register(0, 0x00)
    
    program = bytes([
        obsidianvm.op_sbox, 0,
        obsidianvm.op_halt,
    ])
    
    vm.load_program(program)
    initial = vm.get_register(0)
    vm.run()
    result = vm.get_register(0)
    
    assert result != initial or initial == result


def test_vm_encryption_program():
    vm = obsidianvm()
    
    data = b"test"
    key = b"0123456789abcdef0123456789abcdef"
    
    data_offset = 0x1000
    key_offset = 0x2000
    output_offset = 0x3000
    
    vm.load_data(data, data_offset)
    vm.load_data(key, key_offset)
    
    program = vm.create_encryption_program(
        data_offset, key_offset, output_offset, len(data)
    )
    
    vm.load_program(program)
    vm.run()
    
    encrypted = vm.get_data(output_offset, len(data))
    assert encrypted != data


def test_vm_reset():
    vm = obsidianvm()
    
    vm.set_register(0, 0xff)
    vm.load_data(b"test", 0x100)
    
    vm.reset()
    
    assert vm.get_register(0) == 0
    assert vm.get_data(0x100, 4) == b"\x00\x00\x00\x00"


if __name__ == "__main__":
    test_vm_registers()
    print("test_vm_registers: passed")
    
    test_vm_memory()
    print("test_vm_memory: passed")
    
    test_vm_xor()
    print("test_vm_xor: passed")
    
    test_vm_add()
    print("test_vm_add: passed")
    
    test_vm_rotation()
    print("test_vm_rotation: passed")
    
    test_vm_sbox()
    print("test_vm_sbox: passed")
    
    test_vm_encryption_program()
    print("test_vm_encryption_program: passed")
    
    test_vm_reset()
    print("test_vm_reset: passed")
    
    print("\nall vm tests passed!")
