use std::collections::HashMap;
use std::iter::FromIterator;
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::sync::Arc;
use std::{error, f32, f64, fmt};
use luaur_rt::{
ffi, ChunkMode, Error, ExternalError, Function, Lua, LuaOptions, Nil, Result, StdLib, Table,
UserData, Value, Variadic,
};
#[test]
fn test_weak_lua() {
let lua = Lua::new();
let weak_lua = lua.weak();
assert!(weak_lua.try_upgrade().is_some());
drop(lua);
assert!(weak_lua.try_upgrade().is_none());
}
#[test]
#[should_panic(expected = "Lua instance is destroyed")]
fn test_weak_lua_panic() {
let lua = Lua::new();
let weak_lua = lua.weak();
drop(lua);
let _ = weak_lua.upgrade();
}
#[test]
fn test_load() -> Result<()> {
let lua = Lua::new();
let func = lua.load("\treturn 1+2").into_function()?;
let result: i32 = func.call(())?;
assert_eq!(result, 3);
assert!(lua.load("").exec().is_ok());
assert!(lua.load("§$%§&$%&").exec().is_err());
Ok(())
}
#[test]
fn test_exec() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
lua.load(
r#"
res = 'foo'..'bar'
"#,
)
.exec()?;
assert_eq!(globals.get::<String>("res")?, "foobar");
let module: Table = lua
.load(
r#"
local module = {}
function module.func()
return "hello"
end
return module
"#,
)
.eval()?;
assert!(module.contains_key("func")?);
assert_eq!(module.get::<Function>("func")?.call::<String>(())?, "hello");
Ok(())
}
#[test]
fn test_eval() -> Result<()> {
let lua = Lua::new();
assert_eq!(lua.load("\t1 + 1").eval::<i32>()?, 2);
assert_eq!(lua.load("false == false").eval::<bool>()?, true);
assert_eq!(lua.load("\nreturn 1 + 2").eval::<i32>()?, 3);
match lua.load("if true then").eval::<()>() {
Err(Error::SyntaxError {
incomplete_input: true,
..
}) => {}
r => panic!(
"expected SyntaxError with incomplete_input=true, got {:?}",
r
),
}
Ok(())
}
#[test]
fn test_replace_globals() -> Result<()> {
let lua = Lua::new();
let globals = lua.create_table();
globals.set("foo", "bar")?;
lua.set_globals(globals.clone())?;
let val = lua.load("return foo").eval::<String>()?;
assert_eq!(val, "bar");
{
lua.sandbox(true)?;
match lua.set_globals(globals) {
Err(Error::RuntimeError(msg))
if msg.contains("cannot change globals in a sandboxed Lua state") => {}
r => panic!("expected RuntimeError(...) with a specific error message, got {r:?}"),
}
}
Ok(())
}
#[test]
fn test_lua_multi() -> Result<()> {
let lua = Lua::new();
lua.load(
r#"
function concat(arg1, arg2)
return arg1 .. arg2
end
function mreturn()
return 1, 2, 3, 4, 5, 6
end
"#,
)
.exec()?;
let globals = lua.globals();
let concat = globals.get::<Function>("concat")?;
let mreturn = globals.get::<Function>("mreturn")?;
assert_eq!(concat.call::<String>(("foo", "bar"))?, "foobar");
let (a, b) = mreturn.call::<(u64, u64)>(())?;
assert_eq!((a, b), (1, 2));
let (a, b, v) = mreturn.call::<(u64, u64, Variadic<u64>)>(())?;
assert_eq!((a, b), (1, 2));
assert_eq!(v[..], [3, 4, 5, 6]);
Ok(())
}
#[test]
fn test_coercion() -> Result<()> {
let lua = Lua::new();
lua.load(
r#"
int = 123
str = "123"
num = 123.0
func = function() end
"#,
)
.exec()?;
let globals = lua.globals();
assert_eq!(globals.get::<String>("int")?, "123");
assert_eq!(globals.get::<i32>("str")?, 123);
assert_eq!(globals.get::<i32>("num")?, 123);
assert!(globals.get::<String>("func").is_err());
Ok(())
}
#[test]
fn test_error() -> Result<()> {
#[derive(Debug)]
pub struct TestError;
impl fmt::Display for TestError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "test error")
}
}
impl error::Error for TestError {}
let lua = Lua::new();
let globals = lua.globals();
lua.load(
r#"
function no_error()
end
function lua_error()
error("this is a lua error")
end
function rust_error()
rust_error_function()
end
function return_error()
local status, res = pcall(rust_error_function)
assert(not status)
return res
end
function return_string_error()
return "this should be converted to an error"
end
function test_pcall()
local testvar = 0
pcall(function(arg)
testvar = testvar + arg
error("should be ignored")
end, 3)
local function handler(err)
if string.match(_VERSION, " 5%.1$")
or string.match(_VERSION, " 5%.2$")
or string.match(_VERSION, "Luau")
then
-- Special case for Lua 5.1/5.2 and Luau
local caps = string.match(err, ': (%d+)$')
if caps then
err = caps
end
end
testvar = testvar + err
return "should be ignored"
end
local status, res = xpcall(function()
error(5)
end, handler)
assert(not status)
if testvar ~= 8 then
error("testvar had the wrong value, pcall / xpcall misbehaving "..testvar)
end
end
function understand_recursion()
understand_recursion()
end
"#,
)
.exec()?;
let rust_error_function =
lua.create_function(|_, ()| -> Result<()> { Err(TestError.into_lua_err()) })?;
globals.set("rust_error_function", rust_error_function)?;
let no_error = globals.get::<Function>("no_error")?;
assert!(no_error.call::<()>(()).is_ok());
let lua_error = globals.get::<Function>("lua_error")?;
match lua_error.call::<()>(()) {
Err(Error::RuntimeError(_)) => {}
Err(e) => panic!("error is not RuntimeError kind, got {:?}", e),
_ => panic!("error not returned"),
}
let rust_error = globals.get::<Function>("rust_error")?;
match rust_error.call::<()>(()) {
Err(Error::CallbackError { .. }) | Err(Error::RuntimeError(_)) => {}
Err(e) => panic!("error is not CallbackError/RuntimeError kind, got {:?}", e),
_ => panic!("error not returned"),
}
let return_error = globals.get::<Function>("return_error")?;
match return_error.call::<Value>(()) {
Ok(Value::Error(_)) | Ok(Value::String(_)) => {}
r => panic!("Value::Error / string error not returned, got {r:?}"),
}
let return_string_error = globals.get::<Function>("return_string_error")?;
assert!(return_string_error.call::<Error>(()).is_ok());
match lua
.load("if you are happy and you know it syntax error")
.exec()
{
Err(Error::SyntaxError {
incomplete_input: false,
..
}) => {}
Err(_) => panic!("error is not LuaSyntaxError::Syntax kind"),
_ => panic!("error not returned"),
}
match lua.load("function i_will_finish_what_i()").exec() {
Err(Error::SyntaxError {
incomplete_input: true,
..
}) => {}
Err(_) => panic!("error is not LuaSyntaxError::IncompleteStatement kind"),
_ => panic!("error not returned"),
}
let test_pcall = globals.get::<Function>("test_pcall")?;
test_pcall.call::<()>(())?;
{
let understand_recursion = globals.get::<Function>("understand_recursion")?;
assert!(understand_recursion.call::<()>(()).is_err());
}
Ok(())
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_safe_integers() -> Result<()> {
const MAX_SAFE_INTEGER: i64 = 2i64.pow(53) - 1;
const MIN_SAFE_INTEGER: i64 = -2i64.pow(53) + 1;
let lua = Lua::new();
let f = lua.load("return ...").into_function()?;
assert_eq!(f.call::<i64>(MAX_SAFE_INTEGER)?, MAX_SAFE_INTEGER);
assert_eq!(f.call::<i64>(MIN_SAFE_INTEGER)?, MIN_SAFE_INTEGER);
{
assert_ne!(f.call::<i64>(MAX_SAFE_INTEGER + 2)?, MAX_SAFE_INTEGER + 2);
assert_ne!(f.call::<i64>(MIN_SAFE_INTEGER - 2)?, MIN_SAFE_INTEGER - 2);
assert_eq!(f.call::<f64>(i64::MAX)?, i64::MAX as f64);
}
Ok(())
}
#[test]
fn test_num_conversion() -> Result<()> {
let lua = Lua::new();
assert_eq!(
lua.coerce_integer(Value::String(lua.create_string("1")))?,
Some(1)
);
assert_eq!(
lua.coerce_integer(Value::String(lua.create_string("1.0")))?,
Some(1)
);
assert_eq!(
lua.coerce_integer(Value::String(lua.create_string("1.5")))?,
None
);
assert_eq!(
lua.coerce_number(Value::String(lua.create_string("1")))?,
Some(1.0)
);
assert_eq!(
lua.coerce_number(Value::String(lua.create_string("1.0")))?,
Some(1.0)
);
assert_eq!(
lua.coerce_number(Value::String(lua.create_string("1.5")))?,
Some(1.5)
);
assert_eq!(lua.load("1.0").eval::<i64>()?, 1);
assert_eq!(lua.load("1.0").eval::<f64>()?, 1.0);
assert_eq!(lua.load("1.0").eval::<String>()?, "1");
assert_eq!(lua.load("1.5").eval::<i64>()?, 1);
assert_eq!(lua.load("1.5").eval::<f64>()?, 1.5);
assert_eq!(lua.load("1.5").eval::<String>()?, "1.5");
assert!(lua.load("-1").eval::<u64>().is_err());
assert_eq!(lua.load("-1").eval::<i64>()?, -1);
assert!(lua.unpack::<u64>(lua.pack(1u128 << 64)?).is_err());
assert!(lua.load("math.huge").eval::<i64>().is_err());
assert_eq!(lua.unpack::<f64>(lua.pack(f32::MAX)?)?, f32::MAX as f64);
assert_eq!(lua.unpack::<f64>(lua.pack(f32::MIN)?)?, f32::MIN as f64);
assert_eq!(lua.unpack::<f32>(lua.pack(f64::MAX)?)?, f32::INFINITY);
assert_eq!(lua.unpack::<f32>(lua.pack(f64::MIN)?)?, f32::NEG_INFINITY);
assert_eq!(lua.unpack::<i128>(lua.pack(1i128 << 64)?)?, 1i128 << 64);
let negative_zero = lua.load("-0.0").eval::<f64>()?;
assert_eq!(negative_zero, 0.0);
assert!(!negative_zero.is_sign_negative());
Ok(())
}
#[test]
fn test_pcall_xpcall() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
assert!(lua.load("pcall()").exec().is_err());
assert!(lua.load("xpcall()").exec().is_err());
assert!(lua.load("xpcall(function() end)").exec().is_err());
let (r, e) = lua
.load("pcall(function(p) return p end, 'foo')")
.eval::<(bool, String)>()?;
assert!(r);
assert_eq!(e, "foo");
let (r, e) = lua
.load("xpcall(function(p) return p end, print, 'foo')")
.eval::<(bool, String)>()?;
assert!(r);
assert_eq!(e, "foo");
lua.load(
r#"
pcall_error = nil
pcall_status, pcall_error = pcall(error, "testerror")
xpcall_error = nil
xpcall_status, _ = xpcall(error, function(err) xpcall_error = err end, "testerror")
"#,
)
.exec()?;
assert_eq!(globals.get::<bool>("pcall_status")?, false);
assert_eq!(globals.get::<String>("pcall_error")?, "testerror");
assert_eq!(globals.get::<bool>("xpcall_statusr")?, false);
assert_eq!(
globals.get::<std::string::String>("xpcall_error")?,
"testerror"
);
lua.load(
r#"
function xpcall_recursion()
xpcall(error, function(err) error(err) end, "testerror")
end
"#,
)
.exec()?;
let _ = globals.get::<Function>("xpcall_recursion")?.call::<()>(());
Ok(())
}
#[test]
fn test_recursive_mut_callback_error() -> Result<()> {
let lua = Lua::new();
let mut v = Some(Box::new(123));
let f = lua.create_function_mut(move |lua, mutate: bool| {
if mutate {
v = None;
} else {
let r = v.as_mut().unwrap();
lua.globals().get::<Function>("f")?.call::<()>(true)?;
println!("Should not get here, mutable aliasing has occurred!");
println!("value at {:p} is {r}", r as *mut _);
}
Ok(())
})?;
lua.globals().set("f", f)?;
match lua.globals().get::<Function>("f")?.call::<()>(false) {
Err(Error::CallbackError { ref cause, .. }) => match *cause.as_ref() {
Error::CallbackError { ref cause, .. } => match *cause.as_ref() {
Error::RecursiveMutCallback { .. } => {}
ref other => panic!("incorrect result: {:?}", other),
},
ref other => panic!("incorrect result: {:?}", other),
},
other => panic!("incorrect result: {:?}", other),
};
Ok(())
}
#[test]
fn test_set_metatable_nil() -> Result<()> {
let lua = Lua::new();
lua.load(
r#"
a = {}
setmetatable(a, nil)
"#,
)
.exec()?;
Ok(())
}
#[test]
fn test_named_registry_value() -> Result<()> {
let lua = Lua::new();
lua.set_named_registry_value("test", 42)?;
let f = lua.create_function(move |lua, ()| {
assert_eq!(lua.named_registry_value::<i32>("test")?, 42);
Ok(())
})?;
f.call::<()>(())?;
lua.unset_named_registry_value("test")?;
match lua.named_registry_value("test")? {
Nil => {}
val => panic!("registry value was not Nil, was {:?}", val),
};
Ok(())
}
#[test]
fn test_registry_value() -> Result<()> {
let lua = Lua::new();
let mut r = Some(lua.create_registry_value(42)?);
let f = lua.create_function_mut(move |lua, ()| {
if let Some(r) = r.take() {
assert_eq!(lua.registry_value::<i32>(&r)?, 42);
lua.remove_registry_value(r).unwrap();
} else {
panic!();
}
Ok(())
})?;
f.call::<()>(())?;
Ok(())
}
#[test]
fn test_drop_registry_value() -> Result<()> {
struct MyUserdata(#[allow(unused)] Arc<()>);
impl UserData for MyUserdata {}
let lua = Lua::new();
let rc = Arc::new(());
let r = lua.create_registry_value(MyUserdata(rc.clone()))?;
assert_eq!(Arc::strong_count(&rc), 2);
drop(r);
lua.expire_registry_values();
lua.gc_collect()?;
assert_eq!(Arc::strong_count(&rc), 1);
Ok(())
}
#[test]
fn test_replace_registry_value() -> Result<()> {
let lua = Lua::new();
let mut key = lua.create_registry_value(42)?;
lua.replace_registry_value(&mut key, "new value")?;
assert_eq!(lua.registry_value::<String>(&key)?, "new value");
lua.replace_registry_value(&mut key, Value::Nil)?;
assert_eq!(lua.registry_value::<Value>(&key)?, Value::Nil);
lua.replace_registry_value(&mut key, 123)?;
assert_eq!(lua.registry_value::<i32>(&key)?, 123);
let mut key2 = lua.create_registry_value(Value::Nil)?;
lua.replace_registry_value(&mut key2, Value::Nil)?;
assert_eq!(lua.registry_value::<Value>(&key2)?, Value::Nil);
lua.replace_registry_value(&mut key2, "abc")?;
assert_eq!(lua.registry_value::<String>(&key2)?, "abc");
Ok(())
}
#[test]
fn test_lua_registry_hash() -> Result<()> {
let lua = Lua::new();
let r1 = Arc::new(lua.create_registry_value("value1")?);
let r2 = Arc::new(lua.create_registry_value("value2")?);
let mut map = HashMap::new();
map.insert(r1.clone(), "value1");
map.insert(r2.clone(), "value2");
assert_eq!(map[&r1], "value1");
assert_eq!(map[&r2], "value2");
Ok(())
}
#[test]
fn test_lua_registry_ownership() -> Result<()> {
let lua1 = Lua::new();
let lua2 = Lua::new();
let r1 = lua1.create_registry_value("hello")?;
let r2 = lua2.create_registry_value("hello")?;
assert!(lua1.owns_registry_value(&r1));
assert!(!lua2.owns_registry_value(&r1));
assert!(lua2.owns_registry_value(&r2));
assert!(!lua1.owns_registry_value(&r2));
Ok(())
}
#[test]
fn test_mismatched_registry_key() -> Result<()> {
let lua1 = Lua::new();
let lua2 = Lua::new();
let r = lua1.create_registry_value("hello")?;
match lua2.remove_registry_value(r) {
Err(Error::MismatchedRegistryKey) => {}
r => panic!("wrong result type for mismatched registry key, {:?}", r),
};
Ok(())
}
#[test]
fn test_registry_value_reuse_deferred() -> Result<()> {
let lua = Lua::new();
let r1 = lua.create_registry_value("value1")?;
let r1_slot = format!("{r1:?}");
let r2 = lua.create_registry_value(Value::Nil)?;
let r2_slot = format!("{r2:?}");
assert_ne!(r1_slot, r2_slot);
assert_eq!(format!("{r1:?}"), r1_slot);
drop((r1, r2));
Ok(())
}
#[test]
#[cfg(not(panic = "abort"))]
fn test_application_data() -> Result<()> {
let lua = Lua::new();
lua.set_app_data("test1");
lua.set_app_data(vec!["test2"]);
let s = lua.app_data_ref::<&str>().unwrap();
let mut v = lua.app_data_mut::<Vec<&str>>().unwrap();
v.push("test3");
assert!(lua.try_set_app_data::<i32>(123).is_err());
match catch_unwind(AssertUnwindSafe(|| lua.set_app_data::<i32>(123))) {
Ok(_) => panic!("expected panic"),
Err(_) => {}
}
match catch_unwind(AssertUnwindSafe(|| lua.remove_app_data::<i32>())) {
Ok(_) => panic!("expected panic"),
Err(_) => {}
}
assert_eq!(format!("{s}"), "test1");
assert_eq!(format!("{s:?}"), "\"test1\"");
assert!(lua.try_app_data_mut::<&str>().is_err());
match catch_unwind(AssertUnwindSafe(|| lua.app_data_mut::<&str>().unwrap())) {
Ok(_) => panic!("expected panic"),
Err(_) => {}
}
assert!(lua.try_app_data_ref::<Vec<&str>>().is_err());
drop((s, v));
let f = lua.create_function(|lua, ()| {
let mut data1 = lua.app_data_mut::<&str>().unwrap();
assert_eq!(*data1, "test1");
*data1 = "test4";
let data2 = lua.app_data_ref::<Vec<&str>>().unwrap();
assert_eq!(*data2, vec!["test2", "test3"]);
Ok(())
})?;
f.call::<()>(())?;
assert_eq!(*lua.app_data_ref::<&str>().unwrap(), "test4");
assert_eq!(
*lua.app_data_ref::<Vec<&str>>().unwrap(),
vec!["test2", "test3"]
);
lua.remove_app_data::<Vec<&str>>();
assert!(matches!(lua.app_data_ref::<Vec<&str>>(), None));
Ok(())
}
#[test]
fn test_rust_function() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
lua.load(
r#"
function lua_function()
return rust_function()
end
-- Test to make sure chunk return is ignored
return 1
"#,
)
.exec()?;
let lua_function = globals.get::<Function>("lua_function")?;
let rust_function = lua.create_function(|_, ()| Ok("hello"))?;
globals.set("rust_function", rust_function)?;
assert_eq!(lua_function.call::<String>(())?, "hello");
Ok(())
}
#[test]
fn test_recursion() -> Result<()> {
let lua = Lua::new();
let f = lua.create_function(move |lua, i: i32| {
if i < 64 {
lua.globals().get::<Function>("f")?.call::<()>(i + 1)?;
}
Ok(())
})?;
lua.globals().set("f", &f)?;
f.call::<()>(1)?;
Ok(())
}
#[test]
fn test_too_many_returns() -> Result<()> {
let lua = Lua::new();
let f = lua.create_function(|_, ()| Ok(Variadic::from_iter(1..1000000)))?;
assert!(f.call::<Variadic<u32>>(()).is_err());
Ok(())
}
#[test]
fn test_too_many_arguments() -> Result<()> {
let lua = Lua::new();
lua.load("function test(...) end").exec()?;
let args = Variadic::from_iter(1..1000000);
assert!(lua
.globals()
.get::<Function>("test")?
.call::<()>(args)
.is_err());
Ok(())
}
#[test]
fn test_too_many_recursions() -> Result<()> {
let lua = Lua::new();
let f =
lua.create_function(move |lua, ()| lua.globals().get::<Function>("f")?.call::<()>(()))?;
lua.globals().set("f", &f)?;
assert!(f.call::<()>(()).is_err());
Ok(())
}
#[test]
fn test_large_args() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
globals.set(
"c",
lua.create_function(|_, args: Variadic<usize>| {
let mut s = 0;
for i in 0..args.len() {
s += i;
assert_eq!(i, args[i]);
}
Ok(s)
})?,
)?;
let f: Function = lua
.load(
r#"
return function(...)
return c(...)
end
"#,
)
.eval()?;
assert_eq!(
f.call::<usize>((0..100).collect::<Variadic<usize>>())?,
4950
);
Ok(())
}
#[test]
fn test_large_args_ref() -> Result<()> {
let lua = Lua::new();
let f = lua.create_function(|_, args: Variadic<String>| {
for i in 0..args.len() {
assert_eq!(args[i], i.to_string());
}
Ok(())
})?;
f.call::<()>((0..100).map(|i| i.to_string()).collect::<Variadic<_>>())?;
Ok(())
}
#[test]
fn test_chunk_env() -> Result<()> {
let lua = Lua::new();
let assert: Function = lua.globals().get("assert")?;
let env1 = lua.create_table();
env1.set("assert", assert.clone())?;
let env2 = lua.create_table();
env2.set("assert", assert)?;
lua.load(
r#"
test_var = 1
"#,
)
.set_environment(env1.clone())
.exec()?;
lua.load(
r#"
assert(test_var == nil)
test_var = 2
"#,
)
.set_environment(env2.clone())
.exec()?;
assert_eq!(lua.load("test_var").set_environment(env1).eval::<i32>()?, 1);
assert_eq!(lua.load("test_var").set_environment(env2).eval::<i32>()?, 2);
Ok(())
}
#[test]
fn test_context_thread() -> Result<()> {
let lua = Lua::new();
let f = lua
.load(
r#"
local thread = ...
assert(coroutine.running() == thread)
"#,
)
.into_function()?;
f.call::<()>(Nil)?;
Ok(())
}
#[test]
fn test_register_module() -> Result<()> {
let lua = Lua::new();
let t = lua.create_table();
t.set("name", "my_module")?;
lua.register_module("@my_module", &t)?;
lua.load(
r#"
local my_module = require("@my_module")
assert(my_module.name == "my_module")
"#,
)
.exec()?;
lua.unload_module("@my_module")?;
lua.load(
r#"
local ok, err = pcall(function() return require("@my_module") end)
assert(not ok)
"#,
)
.exec()?;
{
let res = lua.register_module("my_module", 123);
assert!(res.is_err());
assert_eq!(
res.unwrap_err().to_string(),
"runtime error: module name must begin with '@'"
);
let res = lua.register_module("@My_Module", &t);
assert!(res.is_ok());
lua.load(
r#"
local my_module = require("@MY_MODule")
assert(my_module.name == "my_module")
"#,
)
.exec()?;
}
Ok(())
}
#[test]
fn test_traceback_deferred() -> Result<()> {
let lua = Lua::new();
let traceback = lua.traceback(Some("error occurred"), 0)?.to_string_lossy();
assert!(traceback.starts_with("error occurred"));
let get_traceback = lua.create_function(|lua, (msg, level): (Option<String>, usize)| {
lua.traceback(msg.as_deref(), level)
})?;
lua.globals().set("get_traceback", get_traceback)?;
let tb: String = lua
.load(
r#"
local function foo()
return get_traceback(nil, 1)
end
local function bar()
return foo()
end
return bar()
"#,
)
.eval()?;
assert!(tb.contains("foo"));
assert!(tb.contains("bar"));
Ok(())
}
#[test]
fn test_multi_states() -> Result<()> {
let lua = Lua::new();
let f = lua.create_function(|_, g: Option<Function>| {
if let Some(g) = g {
g.call::<()>(())?;
}
Ok(())
})?;
lua.globals().set("f", f)?;
lua.load("f(function() coroutine.wrap(function() f() end)() end)")
.exec()?;
Ok(())
}
#[test]
fn test_exec_raw() -> Result<()> {
let lua = Lua::new();
let sum = lua.create_function(|_, args: Variadic<i32>| {
let mut sum = 0;
for i in args {
sum += i;
}
Ok(sum)
})?;
lua.globals().set("sum", sum)?;
let n: i32 = unsafe {
lua.exec_raw((), |state| {
ffi::lua_getglobal(state, b"sum\0".as_ptr() as _);
ffi::lua_pushinteger(state, 1);
ffi::lua_pushinteger(state, 7);
ffi::lua_call(state, 2, 1);
})
}?;
assert_eq!(n, 8);
let res: Result<()> = unsafe {
lua.exec_raw("test error", |state| {
ffi::lua_error(state);
})
};
assert!(matches!(res, Err(Error::RuntimeError(err)) if err.contains("test error")));
Ok(())
}
#[test]
fn test_gc_drop_ref_thread() -> Result<()> {
let lua = Lua::new();
let t = lua.create_table();
lua.create_function(move |_, ()| {
_ = &t;
Ok(())
})?;
for _ in 0..10000 {
lua.create_table();
}
Ok(())
}
#[test]
#[cfg(feature = "send")]
fn test_multi_thread_deferred() -> Result<()> {
let lua = Lua::new();
lua.globals().set("i", 0)?;
let func = lua.load("i = i + 1").into_function()?;
let lua = std::thread::spawn(move || {
for _ in 0..10 {
func.call::<()>(()).unwrap();
}
lua
})
.join()
.unwrap();
assert_eq!(lua.globals().get::<i32>("i")?, 10);
Ok(())
}
#[test]
fn test_load_mode_deferred() -> Result<()> {
let lua = unsafe { Lua::unsafe_new() };
assert_eq!(
lua.load("1 + 1").set_mode(ChunkMode::Text).eval::<i32>()?,
2
);
assert_eq!(
lua.load("return 1 + 1")
.set_mode(ChunkMode::Binary)
.eval::<i32>()?,
2
);
Ok(())
}
#[test]
#[cfg(not(panic = "abort"))]
fn test_panic_deferred() -> Result<()> {
let lua = Lua::new_with(StdLib::ALL_SAFE, LuaOptions::default())?;
let rust_panic_function =
lua.create_function(|_, ()| -> Result<()> { panic!("rust panic") })?;
lua.globals()
.set("rust_panic_function", rust_panic_function)?;
match lua.load("rust_panic_function()").exec() {
Err(Error::RuntimeError(msg)) => assert!(msg.contains("rust panic")),
other => panic!("expected RuntimeError carrying the panic, got {other:?}"),
}
let caught: bool = lua
.load("local ok = pcall(rust_panic_function); return not ok")
.eval()?;
assert!(caught);
Ok(())
}
#[test]
fn test_c_function_deferred() -> Result<()> {
let lua = Lua::new();
unsafe fn c_function(state: *mut luaur_rt::lua_State) -> std::os::raw::c_int {
unsafe {
ffi::lua_pushboolean(state, 1);
ffi::lua_setglobal(state, b"c_function\0" as *const _ as *const _);
}
0
}
let func = unsafe { lua.create_c_function(Some(c_function))? };
func.call::<()>(())?;
assert_eq!(lua.globals().get::<bool>("c_function")?, true);
Ok(())
}
#[test]
fn test_inspect_stack_deferred() -> Result<()> {
let lua = Lua::new();
let logline = lua.create_function(|lua, msg: String| {
let r = lua
.inspect_stack(1)
.map(|debug| {
let source = debug.short_src().unwrap_or("?").to_string();
let line = debug.current_line().unwrap_or(-1);
format!("{}:{} {}", source, line, msg)
})
.unwrap();
Ok(r)
})?;
lua.globals().set("logline", logline)?;
let line: String = lua
.load(
r#"
local function foo()
return logline("hello")
end
return foo()
"#,
)
.set_name("chunk")
.eval()?;
assert!(line.contains("hello"));
assert!(line.contains(':'));
Ok(())
}