use aegis_vm::engine::execute_with_natives;
use aegis_vm::native::{NativeRegistry, NativeRegistryBuilder, standard_ids};
use aegis_vm::build_config::opcodes::{stack, native, exec};
#[test]
fn test_native_call_no_args() {
let mut registry = NativeRegistry::new();
registry.register(0, |_args| 42).unwrap();
let code = vec![
native::NATIVE_CALL, 0, 0, exec::HALT,
];
let result = execute_with_natives(&code, &[], ®istry).unwrap();
assert_eq!(result, 42);
}
#[test]
fn test_native_call_one_arg() {
let mut registry = NativeRegistry::new();
registry.register(1, |args| args[0] * 2).unwrap();
let code = vec![
stack::PUSH_IMM8, 21,
native::NATIVE_CALL, 1, 1, exec::HALT,
];
let result = execute_with_natives(&code, &[], ®istry).unwrap();
assert_eq!(result, 42);
}
#[test]
fn test_native_call_two_args() {
let mut registry = NativeRegistry::new();
registry.register(2, |args| args[0] + args[1]).unwrap();
let code = vec![
stack::PUSH_IMM8, 30,
stack::PUSH_IMM8, 12,
native::NATIVE_CALL, 2, 2,
exec::HALT,
];
let result = execute_with_natives(&code, &[], ®istry).unwrap();
assert_eq!(result, 42);
}
#[test]
fn test_native_call_many_args() {
let mut registry = NativeRegistry::new();
registry.register(3, |args| args.iter().sum()).unwrap();
let code = vec![
stack::PUSH_IMM8, 1,
stack::PUSH_IMM8, 2,
stack::PUSH_IMM8, 3,
stack::PUSH_IMM8, 4,
stack::PUSH_IMM8, 5,
native::NATIVE_CALL, 3, 5,
exec::HALT,
];
let result = execute_with_natives(&code, &[], ®istry).unwrap();
assert_eq!(result, 15);
}
#[test]
fn test_multiple_native_calls() {
let mut registry = NativeRegistry::new();
registry.register(0, |_| 10).unwrap();
registry.register(1, |_| 20).unwrap();
registry.register(2, |args| args[0] + args[1]).unwrap();
let code = vec![
native::NATIVE_CALL, 0, 0, native::NATIVE_CALL, 1, 0, native::NATIVE_CALL, 2, 2, exec::HALT,
];
let result = execute_with_natives(&code, &[], ®istry).unwrap();
assert_eq!(result, 30);
}
#[test]
fn test_native_call_chain() {
let mut registry = NativeRegistry::new();
registry.register(0, |args| args[0] * 2).unwrap();
let code = vec![
stack::PUSH_IMM8, 1,
native::NATIVE_CALL, 0, 1, native::NATIVE_CALL, 0, 1, native::NATIVE_CALL, 0, 1, native::NATIVE_CALL, 0, 1, native::NATIVE_CALL, 0, 1, exec::HALT,
];
let result = execute_with_natives(&code, &[], ®istry).unwrap();
assert_eq!(result, 32);
}
#[test]
fn test_registry_builder() {
let registry = NativeRegistryBuilder::new()
.with_function(0, |_| 100)
.with_function(1, |args| args.get(0).copied().unwrap_or(0) + 1)
.with_hash()
.build();
let code1 = vec![native::NATIVE_CALL, 0, 0, exec::HALT];
assert_eq!(execute_with_natives(&code1, &[], ®istry).unwrap(), 100);
let code2 = vec![
stack::PUSH_IMM8, 41,
native::NATIVE_CALL, 1, 1,
exec::HALT,
];
assert_eq!(execute_with_natives(&code2, &[], ®istry).unwrap(), 42);
}
#[test]
fn test_hash_function() {
let registry = NativeRegistryBuilder::new()
.with_hash()
.build();
let code = vec![
stack::PUSH_IMM8, 42,
native::NATIVE_CALL, standard_ids::HASH_FNV1A, 1,
exec::HALT,
];
let result = execute_with_natives(&code, &[], ®istry).unwrap();
assert_ne!(result, 0);
let result2 = execute_with_natives(&code, &[], ®istry).unwrap();
assert_eq!(result, result2);
}
#[test]
fn test_native_function_not_found() {
let registry = NativeRegistry::new();
let code = vec![
native::NATIVE_CALL, 99, 0, exec::HALT,
];
let result = execute_with_natives(&code, &[], ®istry);
assert!(result.is_err());
}
#[test]
fn test_anticheat_simulation() {
let mut registry = NativeRegistry::new();
registry.register(standard_ids::CHECK_ROOT, |_| 0).unwrap(); registry.register(standard_ids::CHECK_EMULATOR, |_| 0).unwrap(); registry.register(standard_ids::CHECK_HOOKS, |_| 10).unwrap(); registry.register(standard_ids::CHECK_DEBUGGER, |_| 0).unwrap();
registry.register(10, |args| args.iter().sum()).unwrap();
let code = vec![
native::NATIVE_CALL, standard_ids::CHECK_ROOT, 0,
native::NATIVE_CALL, standard_ids::CHECK_EMULATOR, 0,
native::NATIVE_CALL, standard_ids::CHECK_HOOKS, 0,
native::NATIVE_CALL, standard_ids::CHECK_DEBUGGER, 0,
native::NATIVE_CALL, 10, 4, exec::HALT,
];
let total_risk = execute_with_natives(&code, &[], ®istry).unwrap();
assert_eq!(total_risk, 10); }
#[test]
fn test_native_result_used_in_computation() {
let mut registry = NativeRegistry::new();
registry.register(0, |_| 50).unwrap(); registry.register(1, |args| if args[0] > 30 { 1 } else { 0 }).unwrap();
let code = vec![
native::NATIVE_CALL, 0, 0, native::NATIVE_CALL, 1, 1, exec::HALT,
];
let result = execute_with_natives(&code, &[], ®istry).unwrap();
assert_eq!(result, 1); }
#[test]
fn test_conditional_jump_with_natives() {
use aegis_vm::build_config::opcodes::control;
let mut registry = NativeRegistry::new();
registry.register(0, |_| 50).unwrap();
let code = vec![
native::NATIVE_CALL, 0, 0, stack::PUSH_IMM8, 30, control::CMP, stack::DROP, stack::DROP, control::JGT, 0x05, 0x00, stack::PUSH_IMM8, 0, control::JMP, 0x02, 0x00, stack::PUSH_IMM8, 1, exec::HALT, ];
let result = execute_with_natives(&code, &[], ®istry).unwrap();
assert_eq!(result, 1); }
#[test]
fn test_conditional_jump_false_branch() {
use aegis_vm::build_config::opcodes::control;
let mut registry = NativeRegistry::new();
registry.register(0, |_| 20).unwrap();
let code = vec![
native::NATIVE_CALL, 0, 0, stack::PUSH_IMM8, 30, control::CMP, stack::DROP, stack::DROP, control::JGT, 0x05, 0x00, stack::PUSH_IMM8, 0, control::JMP, 0x02, 0x00, stack::PUSH_IMM8, 1, exec::HALT, ];
let result = execute_with_natives(&code, &[], ®istry).unwrap();
assert_eq!(result, 0); }
#[test]
fn test_native_with_vm_arithmetic() {
use aegis_vm::build_config::opcodes::arithmetic;
let mut registry = NativeRegistry::new();
registry.register(0, |_| 100).unwrap();
let code = vec![
native::NATIVE_CALL, 0, 0, stack::PUSH_IMM8, 2,
arithmetic::MUL, stack::PUSH_IMM8, 5,
arithmetic::ADD, exec::HALT,
];
let result = execute_with_natives(&code, &[], ®istry).unwrap();
assert_eq!(result, 205);
}
#[test]
fn test_native_with_closure_state() {
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
let counter = Arc::new(AtomicU64::new(0));
let counter_clone = counter.clone();
let mut registry = NativeRegistry::new();
registry.register(0, move |_| {
counter_clone.fetch_add(1, Ordering::SeqCst) + 1
}).unwrap();
let code = vec![
native::NATIVE_CALL, 0, 0, stack::DROP,
native::NATIVE_CALL, 0, 0, stack::DROP,
native::NATIVE_CALL, 0, 0, exec::HALT,
];
let result = execute_with_natives(&code, &[], ®istry).unwrap();
assert_eq!(result, 3);
assert_eq!(counter.load(Ordering::SeqCst), 3);
}