#[cfg(test)]
use alloc::{format, string::String, string::ToString, vec, vec::Vec};
use crate::atom::AtomTableOps;
use crate::testing::mocks::*;
use crate::term::{Term, TermValue, NifResult, NifError, Context};
#[cfg(test)]
pub extern "C" fn test_add_nif(
_ctx: *mut Context,
_argc: i32,
_argv: *const Term
) -> Term {
Term::from_raw(42 << 4 | 0xF) }
#[cfg(test)]
pub extern "C" fn test_string_nif(
_ctx: *mut Context,
_argc: i32,
_argv: *const Term
) -> Term {
Term::from_raw(0x3B) }
#[cfg(test)]
pub extern "C" fn test_list_nif(
_ctx: *mut Context,
_argc: i32,
_argv: *const Term
) -> Term {
Term::from_raw(0x3B) }
#[cfg(test)]
pub fn test_nif_init(_ctx: &mut Context) {
}
#[cfg(test)]
pub struct NifCallSimulator {
pub call_count: u32,
pub last_function: Option<String>,
pub last_args: Vec<TermValue>,
}
#[cfg(test)]
impl NifCallSimulator {
pub fn new() -> Self {
Self {
call_count: 0,
last_function: None,
last_args: Vec::new(),
}
}
pub fn simulate_call(&mut self, function_name: &str, args: Vec<TermValue>) -> NifResult<TermValue> {
self.call_count += 1;
self.last_function = Some(function_name.to_string());
self.last_args = args.clone();
match function_name {
"add" => {
if args.len() != 2 {
return Err(NifError::BadArity);
}
let a = args[0].as_int().ok_or(NifError::BadArg)?;
let b = args[1].as_int().ok_or(NifError::BadArg)?;
Ok(TermValue::int(a + b))
}
"list_length" => {
if args.len() != 1 {
return Err(NifError::BadArity);
}
let length = args[0].list_length();
Ok(TermValue::int(length as i32))
}
"make_tuple" => {
Ok(TermValue::tuple(args))
}
"error_function" => {
Err(NifError::BadArg)
}
_ => Err(NifError::Other("unknown function")),
}
}
pub fn reset(&mut self) {
self.call_count = 0;
self.last_function = None;
self.last_args.clear();
}
}
#[cfg(test)]
pub fn mock_nif_resolver(name: &str) -> Option<*const core::ffi::c_void> {
match name {
"test_add" => Some(test_add_nif as *const () as *const core::ffi::c_void),
"test_string" => Some(test_string_nif as *const () as *const core::ffi::c_void),
"test_list" => Some(test_list_nif as *const () as *const core::ffi::c_void),
_ => None,
}
}
#[cfg(test)]
#[macro_export]
macro_rules! test_nif_collection {
() => {
$crate::nif_collection!(
test_collection,
init = test_nif_init,
nifs = [
("add", 2, test_add_nif),
("string_op", 1, test_string_nif),
("list_op", 1, test_list_nif),
]
);
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_nif_call_simulator_creation() {
let simulator = NifCallSimulator::new();
assert_eq!(simulator.call_count, 0);
assert!(simulator.last_function.is_none());
assert_eq!(simulator.last_args.len(), 0);
}
#[test]
fn test_nif_call_simulator_add_function() {
let mut simulator = NifCallSimulator::new();
let args = vec![TermValue::int(10), TermValue::int(20)];
let result = simulator.simulate_call("add", args).unwrap();
assert_eq!(result, TermValue::int(30));
assert_eq!(simulator.call_count, 1);
assert_eq!(simulator.last_function.as_ref().unwrap(), "add");
assert_eq!(simulator.last_args.len(), 2);
}
#[test]
fn test_nif_call_simulator_bad_arity() {
let mut simulator = NifCallSimulator::new();
let args = vec![TermValue::int(10)]; let result = simulator.simulate_call("add", args);
assert_eq!(result, Err(NifError::BadArity));
assert_eq!(simulator.call_count, 1);
}
#[test]
fn test_nif_call_simulator_bad_args() {
let mut simulator = NifCallSimulator::new();
let atom_table = MockAtomTable::new();
let atom = TermValue::atom("not_a_number", &atom_table);
let args = vec![TermValue::int(10), atom];
let result = simulator.simulate_call("add", args);
assert_eq!(result, Err(NifError::BadArg));
}
#[test]
fn test_nif_call_simulator_list_length() {
let mut simulator = NifCallSimulator::new();
let list = TermValue::list(vec![
TermValue::int(1),
TermValue::int(2),
TermValue::int(3),
]);
let args = vec![list];
let result = simulator.simulate_call("list_length", args).unwrap();
assert_eq!(result, TermValue::int(3));
assert_eq!(simulator.last_function.as_ref().unwrap(), "list_length");
}
#[test]
fn test_nif_call_simulator_make_tuple() {
let mut simulator = NifCallSimulator::new();
let args = vec![
TermValue::int(1),
TermValue::int(2),
TermValue::int(3),
];
let result = simulator.simulate_call("make_tuple", args.clone()).unwrap();
if let Some(elements) = result.as_tuple() {
assert_eq!(elements.len(), 3);
assert_eq!(elements[0], TermValue::int(1));
assert_eq!(elements[1], TermValue::int(2));
assert_eq!(elements[2], TermValue::int(3));
} else {
panic!("Expected tuple result");
}
}
#[test]
fn test_nif_call_simulator_error_function() {
let mut simulator = NifCallSimulator::new();
let result = simulator.simulate_call("error_function", vec![]);
assert_eq!(result, Err(NifError::BadArg));
}
#[test]
fn test_nif_call_simulator_unknown_function() {
let mut simulator = NifCallSimulator::new();
let result = simulator.simulate_call("unknown_func", vec![]);
assert_eq!(result, Err(NifError::Other("unknown function")));
}
#[test]
fn test_nif_call_simulator_reset() {
let mut simulator = NifCallSimulator::new();
simulator.simulate_call("add", vec![TermValue::int(1), TermValue::int(2)]).unwrap();
assert_eq!(simulator.call_count, 1);
assert!(simulator.last_function.is_some());
simulator.reset();
assert_eq!(simulator.call_count, 0);
assert!(simulator.last_function.is_none());
assert_eq!(simulator.last_args.len(), 0);
}
#[test]
fn test_mock_nif_resolver() {
let add_ptr = mock_nif_resolver("test_add");
assert!(add_ptr.is_some());
let string_ptr = mock_nif_resolver("test_string");
assert!(string_ptr.is_some());
let list_ptr = mock_nif_resolver("test_list");
assert!(list_ptr.is_some());
let unknown_ptr = mock_nif_resolver("unknown");
assert!(unknown_ptr.is_none());
}
#[test]
fn test_nif_function_pointers() {
let add_fn_ptr = test_add_nif as *const ();
let string_fn_ptr = test_string_nif as *const ();
let list_fn_ptr = test_list_nif as *const ();
assert!(!add_fn_ptr.is_null());
assert!(!string_fn_ptr.is_null());
assert!(!list_fn_ptr.is_null());
assert_ne!(add_fn_ptr, string_fn_ptr);
assert_ne!(string_fn_ptr, list_fn_ptr);
}
#[test]
fn test_term_raw_values() {
use core::ptr;
let result = test_add_nif(ptr::null_mut(), 0, ptr::null());
assert_eq!(result.raw(), 42 << 4 | 0xF);
let nil_result = test_string_nif(ptr::null_mut(), 0, ptr::null());
assert_eq!(nil_result.raw(), 0x3B); }
#[test]
fn test_nif_collection_macro_components() {
let moniker = "test_collection";
let expected_init_name = format!("{}_nif_init", moniker);
let expected_resolver_name = format!("{}_get_nif", moniker);
assert_eq!(expected_init_name, "test_collection_nif_init");
assert_eq!(expected_resolver_name, "test_collection_get_nif");
let _init_fn: fn(&mut Context) = test_nif_init;
let _add_fn: extern "C" fn(*mut Context, i32, *const Term) -> Term = test_add_nif;
}
#[test]
fn test_nif_collection_function_list() {
let functions = vec![
("add", 2),
("string_op", 1),
("list_op", 1),
];
assert_eq!(functions.len(), 3);
assert_eq!(functions[0], ("add", 2));
assert_eq!(functions[1], ("string_op", 1));
assert_eq!(functions[2], ("list_op", 1));
}
#[test]
fn test_multiple_nif_calls() {
let mut simulator = NifCallSimulator::new();
simulator.simulate_call("add", vec![TermValue::int(1), TermValue::int(2)]).unwrap();
simulator.simulate_call("list_length", vec![TermValue::list(vec![TermValue::int(1)])]).unwrap();
simulator.simulate_call("add", vec![TermValue::int(5), TermValue::int(10)]).unwrap();
assert_eq!(simulator.call_count, 3);
assert_eq!(simulator.last_function.as_ref().unwrap(), "add");
assert_eq!(simulator.last_args[0], TermValue::int(5));
assert_eq!(simulator.last_args[1], TermValue::int(10));
}
#[test]
fn test_nif_error_handling_patterns() {
let mut simulator = NifCallSimulator::new();
let error_cases = vec![
("add", vec![TermValue::int(1)], NifError::BadArity),
("unknown_func", vec![], NifError::Other("unknown function")),
("error_function", vec![], NifError::BadArg),
];
for (func_name, args, expected_error) in error_cases {
let result = simulator.simulate_call(func_name, args);
assert_eq!(result, Err(expected_error));
}
}
#[test]
fn test_nif_collection_registration_data() {
let collection_name = "test_collection";
let nif_definitions = vec![
("add", 2, "test_add_nif"),
("string_op", 1, "test_string_nif"),
("list_op", 1, "test_list_nif"),
];
assert_eq!(collection_name, "test_collection");
assert_eq!(nif_definitions.len(), 3);
for (name, arity, func_name) in &nif_definitions {
assert!(!name.is_empty());
assert!(*arity > 0);
assert!(!func_name.is_empty());
}
}
#[test]
fn test_link_section_attributes() {
#[cfg(any(target_os = "macos", target_os = "ios"))]
let expected_section = "__DATA,.nif_collection";
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
let expected_section = ".nif_collection";
#[cfg(any(target_os = "macos", target_os = "ios"))]
assert_eq!(expected_section, "__DATA,.nif_collection");
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
assert_eq!(expected_section, ".nif_collection");
}
}