use crate::lua_vm::{LuaVM, SafeOption};
#[test]
fn test_function_with_default_return() {
let mut vm = LuaVM::new(SafeOption::default());
vm.open_stdlib(crate::stdlib::Stdlib::All).unwrap();
let result = vm.execute(
r#"
local function no_return() end
local result = no_return()
assert(result == nil)
"#,
);
assert!(result.is_ok());
}
#[test]
fn test_function_multiple_returns() {
let mut vm = LuaVM::new(SafeOption::default());
vm.open_stdlib(crate::stdlib::Stdlib::All).unwrap();
let result = vm.execute(
r#"
local function multi()
return 1, 2, 3, 4, 5
end
local a, b, c, d, e = multi()
assert(a == 1 and b == 2 and c == 3 and d == 4 and e == 5)
"#,
);
assert!(result.is_ok());
}
#[test]
fn test_function_variable_returns() {
let mut vm = LuaVM::new(SafeOption::default());
vm.open_stdlib(crate::stdlib::Stdlib::All).unwrap();
let result = vm.execute(
r#"
local function ret_n(n)
if n == 1 then return "one"
elseif n == 2 then return "two", 2
else return "three", 3, 3.0
end
end
local a = ret_n(1)
assert(a == "one")
local b, c = ret_n(2)
assert(b == "two" and c == 2)
local d, e, f = ret_n(3)
assert(d == "three" and e == 3 and f == 3.0)
"#,
);
assert!(result.is_ok());
}
#[test]
fn test_function_tail_call() {
let mut vm = LuaVM::new(SafeOption::default());
vm.open_stdlib(crate::stdlib::Stdlib::All).unwrap();
let result = vm.execute(
r#"
local function countdown(n)
if n == 0 then
return "done"
else
return countdown(n - 1)
end
end
assert(countdown(100) == "done")
"#,
);
assert!(result.is_ok());
}
#[test]
fn test_function_vararg_basic() {
let mut vm = LuaVM::new(SafeOption::default());
vm.open_stdlib(crate::stdlib::Stdlib::All).unwrap();
let result = vm.execute(
r#"
local function sum(...)
local total = 0
for i, v in ipairs({...}) do
total = total + v
end
return total
end
assert(sum(1, 2, 3, 4, 5) == 15)
assert(sum(10, 20) == 30)
"#,
);
if let Err(e) = &result {
eprintln!("Error: {:?}", e);
}
assert!(result.is_ok());
}
#[test]
fn test_function_vararg_count() {
let mut vm = LuaVM::new(SafeOption::default());
vm.open_stdlib(crate::stdlib::Stdlib::All).unwrap();
let result = vm.execute(
r#"
local function count_args(...)
return select('#', ...)
end
assert(count_args(1, 2, 3) == 3)
assert(count_args() == 0)
assert(count_args(nil, nil, 1) == 3)
"#,
);
if let Err(e) = &result {
eprintln!("Error: {:?}", e);
}
assert!(result.is_ok());
}
#[test]
fn test_function_vararg_select() {
let mut vm = LuaVM::new(SafeOption::default());
vm.open_stdlib(crate::stdlib::Stdlib::All).unwrap();
let result = vm.execute(
r#"
local function get_nth(n, ...)
return select(n, ...)
end
assert(get_nth(2, 10, 20, 30) == 20)
assert(get_nth(3, "a", "b", "c", "d") == "c")
"#,
);
if let Err(e) = &result {
eprintln!("Error: {:?}", e);
}
assert!(result.is_ok());
}
#[test]
fn test_function_nested_calls() {
let mut vm = LuaVM::new(SafeOption::default());
vm.open_stdlib(crate::stdlib::Stdlib::All).unwrap();
let result = vm.execute(
r#"
local function add(a, b) return a + b end
local function mul(a, b) return a * b end
local function calc(a, b, c)
return add(mul(a, b), c)
end
assert(calc(2, 3, 4) == 10)
assert(calc(5, 4, 1) == 21)
"#,
);
assert!(result.is_ok());
}
#[test]
fn test_function_as_parameter() {
let mut vm = LuaVM::new(SafeOption::default());
vm.open_stdlib(crate::stdlib::Stdlib::All).unwrap();
let result = vm.execute(
r#"
local function apply(f, x, y)
return f(x, y)
end
local function add(a, b) return a + b end
local function mul(a, b) return a * b end
assert(apply(add, 3, 4) == 7)
assert(apply(mul, 3, 4) == 12)
"#,
);
assert!(result.is_ok());
}
#[test]
fn test_function_returning_function() {
let mut vm = LuaVM::new(SafeOption::default());
vm.open_stdlib(crate::stdlib::Stdlib::All).unwrap();
let result = vm.execute(
r#"
local function get_operation(op)
if op == "add" then
return function(a, b) return a + b end
elseif op == "mul" then
return function(a, b) return a * b end
end
end
local add = get_operation("add")
local mul = get_operation("mul")
assert(add(3, 4) == 7)
assert(mul(3, 4) == 12)
"#,
);
assert!(result.is_ok());
}
#[test]
fn test_function_table_of_functions() {
let mut vm = LuaVM::new(SafeOption::default());
vm.open_stdlib(crate::stdlib::Stdlib::All).unwrap();
let result = vm.execute(
r#"
local ops = {
add = function(a, b) return a + b end,
sub = function(a, b) return a - b end,
mul = function(a, b) return a * b end,
div = function(a, b) return a / b end
}
assert(ops.add(10, 5) == 15)
assert(ops.sub(10, 5) == 5)
assert(ops.mul(10, 5) == 50)
assert(ops.div(10, 5) == 2)
"#,
);
assert!(result.is_ok());
}
#[test]
fn test_function_anonymous_immediate_call() {
let mut vm = LuaVM::new(SafeOption::default());
vm.open_stdlib(crate::stdlib::Stdlib::All).unwrap();
let result = vm.execute(
r#"
local result = (function(x, y)
return x * x + y * y
end)(3, 4)
assert(result == 25)
"#,
);
assert!(result.is_ok());
}
#[test]
fn test_function_method_call_chain() {
let mut vm = LuaVM::new(SafeOption::default());
vm.open_stdlib(crate::stdlib::Stdlib::All).unwrap();
let result = vm.execute(
r#"
local obj = {value = 10}
function obj:add(n)
self.value = self.value + n
return self
end
function obj:mul(n)
self.value = self.value * n
return self
end
function obj:get()
return self.value
end
obj:add(5):mul(2):add(10)
assert(obj:get() == 40)
"#,
);
assert!(result.is_ok());
}
#[test]
fn test_function_local_function_scope() {
let mut vm = LuaVM::new(SafeOption::default());
vm.open_stdlib(crate::stdlib::Stdlib::All).unwrap();
let result = vm.execute(
r#"
local function outer()
local value = 10
local function inner()
return value * 2
end
return inner()
end
assert(outer() == 20)
"#,
);
assert!(result.is_ok());
}
#[test]
fn test_function_early_return() {
let mut vm = LuaVM::new(SafeOption::default());
vm.open_stdlib(crate::stdlib::Stdlib::All).unwrap();
let result = vm.execute(
r#"
local function check(x)
if x < 0 then return "negative" end
if x == 0 then return "zero" end
return "positive"
end
assert(check(-5) == "negative")
assert(check(0) == "zero")
assert(check(5) == "positive")
"#,
);
assert!(result.is_ok());
}
#[test]
fn test_function_multiple_definitions() {
let mut vm = LuaVM::new(SafeOption::default());
vm.open_stdlib(crate::stdlib::Stdlib::All).unwrap();
let result = vm.execute(
r#"
local function foo() return "first" end
local x = foo()
local function foo() return "second" end
local y = foo()
assert(x == "first" and y == "second")
"#,
);
assert!(result.is_ok());
}
#[test]
fn test_function_pcall_wrapper() {
let mut vm = LuaVM::new(SafeOption::default());
vm.open_stdlib(crate::stdlib::Stdlib::All).unwrap();
let result = vm.execute(
r#"
local function safe_divide(a, b)
if b == 0 then
error("division by zero")
end
return a / b
end
local ok, result = pcall(safe_divide, 10, 2)
assert(ok and result == 5)
local ok2, err = pcall(safe_divide, 10, 0)
assert(not ok2)
"#,
);
assert!(result.is_ok());
}
#[test]
fn test_function_ipairs_wrapper() {
let mut vm = LuaVM::new(SafeOption::default());
vm.open_stdlib(crate::stdlib::Stdlib::All).unwrap();
let result = vm.execute(
r#"
local function map(t, f)
local result = {}
for i, v in ipairs(t) do
result[i] = f(v)
end
return result
end
local squares = map({1, 2, 3, 4}, function(x) return x * x end)
assert(squares[1] == 1 and squares[2] == 4 and squares[3] == 9 and squares[4] == 16)
"#,
);
assert!(result.is_ok());
}
#[test]
fn test_function_reduce() {
let mut vm = LuaVM::new(SafeOption::default());
vm.open_stdlib(crate::stdlib::Stdlib::All).unwrap();
let result = vm.execute(
r#"
local function reduce(t, f, init)
local acc = init
for i, v in ipairs(t) do
acc = f(acc, v)
end
return acc
end
local sum = reduce({1, 2, 3, 4, 5}, function(a, b) return a + b end, 0)
assert(sum == 15)
"#,
);
assert!(result.is_ok());
}