use sandkiste::prelude::*;
#[cfg(feature = "Lua5_3")]
use super::v5_3::*;
#[cfg(feature = "Lua5_4")]
use super::v5_4::*;
#[test]
fn test_memory_limit() {
let m = LuaMachine::new();
m.set_memory_limit(1024 * 1024);
const CODE: &'static str = "t = {}; for i = 1, 1024*1024 do t[i] = true end";
let err = m.compile(None, CODE).unwrap().call([]).unwrap_err();
assert_eq!(err.kind, MachineErrorKind::Memory);
}
#[test]
fn test_execution_limit() {
let m = LuaMachine::new();
m.set_execution_limit(16 * 1024);
const CODE: &'static str = "for i = 1, 1024*1024 do end";
let err = m.compile(None, CODE).unwrap().call([]).unwrap_err();
assert_eq!(err.kind, MachineErrorKind::ExecLimit);
}
#[test]
fn test_stdlib() {
let m = LuaMachine::new();
m.load_stdlib().unwrap();
const CODE: &'static str = "os.execute('echo'); return math.sqrt(4.0)";
let d = m.compile(None, CODE).unwrap().call_1ret([]).unwrap();
assert_eq!(d.try_as_f64().unwrap(), 2.0);
}
fn assert_std_sealed<'a>(m: &LuaMachine<'a>) {
const CODE1: &'static str = "os.execute('echo')";
let err = m.compile(None, CODE1).unwrap().call([]).unwrap_err();
assert_eq!(err.kind, MachineErrorKind::Runtime);
const CODE2: &'static str = "return math.sqrt(9.0)";
let d = m.compile(None, CODE2).unwrap().call_1ret([]).unwrap();
assert_eq!(d.try_as_f64().unwrap(), 3.0);
const CODE3: &'static str = "\
local expected = {
_G = true,\n\
_VERSION = true,\n\
assert = true,\n\
bit32 = 'optional', -- deprecated in Lua 5.3 and not in Lua 5.4\n\
collectgarbage = true,\n\
error = true,\n\
getmetatable = true,\n\
ipairs = true,\n\
next = true,\n\
pairs = true,\n\
rawequal = true,\n\
rawget = true,\n\
rawlen = true,\n\
rawset = true,\n\
select = true,\n\
setmetatable = true,\n\
tonumber = true,\n\
tostring = true,\n\
type = true,\n\
warn = 'optional', -- not in Lua 5.3\n\
math = true,\n\
string = true,\n\
table = true,\n\
utf8 = true,\n\
}\n\
for key in pairs(_G) do\n\
if not expected[key] then error('unexpected variable set: ' .. tostring(key)) end\n\
expected[key] = nil\n\
end\n\
for k, v in pairs(expected) do\n\
if v == 'optional' then\n\
expected[k] = nil\n\
end\n\
end\n\
local found = next(expected)\n\
if found ~= nil then \n\
error('variable missing: ' .. tostring(found))\n\
end\n\
return 'noerror'
";
let d = m.compile(None, CODE3).unwrap().call_1ret([]).unwrap();
assert_eq!(d.try_as_str().unwrap(), "noerror");
}
#[test]
fn test_stdlib_sealed() {
let m = LuaMachine::new();
m.load_stdlib_sealed().unwrap();
assert_std_sealed(&m);
}
#[test]
fn test_seal() {
let m = LuaMachine::new();
m.load_stdlib().unwrap();
m.seal().unwrap();
assert_std_sealed(&m);
}
#[test]
fn test_error_reporting() {
let m = LuaMachine::new();
m.load_stdlib().unwrap();
let err: MachineError = m
.compile(Some("testchunk".to_string()), "\n\nerror('testerror')")
.unwrap()
.call([])
.unwrap_err();
assert_eq!(err.chunk_name.as_deref(), Some("testchunk"));
assert_eq!(err.line, Some(3));
}
#[test]
fn test_callbacks() {
use std::cell::RefCell;
let output = RefCell::new(String::new());
let m = LuaMachine::new();
m.load_stdlib_sealed().unwrap();
let print = m
.callback_1arg(|s| {
let s = s.try_as_str()?;
output.borrow_mut().push_str(s);
Ok([])
})
.unwrap();
let doubleadd = m
.callback_2arg(|a, b| {
let a = a.try_as_i32()?;
let b = b.try_as_i32()?;
Ok([LuaDatum::from(2 * (a + b))])
})
.unwrap();
m.compile(None, "print, doubleadd = ...")
.unwrap()
.call([print, doubleadd])
.unwrap();
const CODE: &'static str = "print('Hello '); print('World'); print(tostring(doubleadd(2, 5)))";
m.compile(None, CODE).unwrap().call([]).unwrap();
drop(m);
let output = output.into_inner();
assert_eq!(output, "Hello World14");
}
#[test]
fn test_array_read() {
let m = LuaMachine::new();
const CODE: &'static str = "return {12, 'thirteen'}";
let d = m.compile(None, CODE).unwrap().call_1ret([]).unwrap();
assert!(d.try_array().is_ok());
assert_eq!(d.array_len().unwrap(), 2);
assert_eq!(d.array_get(0).unwrap().try_as_i32().unwrap(), 12);
assert_eq!(d.array_get(1).unwrap().try_as_str().unwrap(), "thirteen");
assert!(d.array_to_vec(1).is_err());
let v = d.array_to_vec(2).unwrap();
assert_eq!(v.len(), 2);
assert_eq!(v[0].try_as_i32().unwrap(), 12);
assert_eq!(v[1].try_as_str().unwrap(), "thirteen");
let mut iter = d.array_to_iter().unwrap();
assert_eq!(iter.next().unwrap().unwrap().try_as_i32().unwrap(), 12);
assert_eq!(
iter.next().unwrap().unwrap().try_as_str().unwrap(),
"thirteen"
);
assert!(iter.next().is_none());
}
#[test]
fn test_array_write() {
let m = LuaMachine::new();
let d = m
.new_array([LuaDatum::from(14), false.into(), "fifteen".into()])
.unwrap();
d.array_set(1, true.into()).unwrap();
d.array_push(LuaDatum::from(0.5)).unwrap();
d.array_push(LuaDatum::from(1.5)).unwrap();
d.array_push(LuaDatum::from(2.5)).unwrap();
d.array_truncate(4).unwrap();
const CODE: &'static str = "\
local t = ...\n\
if\n\
#t == 4 and\n\
t[1] == 14 and\n\
t[2] == true and\n\
t[3] == 'fifteen' and\n\
t[4] == 0.5\n\
then\n\
return 'magictoken'\n\
end\n\
";
assert_eq!(
m.compile(None, CODE)
.unwrap()
.call_1ret([d])
.unwrap()
.try_as_str()
.unwrap(),
"magictoken"
);
}
#[test]
fn test_string_map_read() {
let m = LuaMachine::new();
const CODE: &'static str = "return {a = 5, b = 20}";
let d = m.compile(None, CODE).unwrap().call_1ret([]).unwrap();
assert!(d.try_string_map().is_ok());
assert_eq!(d.string_map_get("a").unwrap().try_as_i32().unwrap(), 5);
assert_eq!(d.string_map_get("b").unwrap().try_as_i32().unwrap(), 20);
match d.string_map_get("c") {
Ok(d) => assert!(d.is_null()),
Err(_) => (),
};
}
#[test]
fn test_string_map_write() {
use std::collections::HashMap;
let mut hash_map: HashMap<&'static str, LuaDatum> = HashMap::new();
hash_map.insert("a", LuaDatum::from(7));
hash_map.insert("b", LuaDatum::from(11));
let m = LuaMachine::new();
let d = m.new_string_map(hash_map).unwrap();
const CODE: &'static str = "local t = ...; return t.a * t.b";
let r = m.compile(None, CODE).unwrap().call_1ret([d]).unwrap();
assert_eq!(r.try_as_i32().unwrap(), 77);
}
#[test]
fn test_globals() {
let m = LuaMachine::new();
m.set("triplenine", LuaDatum::from(999)).unwrap();
m.set("minusone", LuaDatum::from(-1)).unwrap();
eprintln!("DEBUG: {:?}", m.get("triplenine"));
assert_eq!(m.get("triplenine").unwrap().try_as_i32().unwrap(), 999);
assert_eq!(m.get("minusone").unwrap().try_as_i32().unwrap(), -1);
}
#[test]
fn test_load_module() {
let m = LuaMachine::new();
let mymod = m.module("mymod").unwrap();
mymod.set("pi_approx", 3.14.into()).unwrap();
mymod
.set(
"double",
m.callback_1arg(|x| Ok([(x.try_as_f64()? * 2.0).into()]))
.unwrap(),
)
.unwrap();
const CODE: &'static str =
"if mymod.pi_approx > 3 and mymod.pi_approx < 4 then return 'pimatch' end";
let d = m.compile(None, CODE).unwrap().call_1ret([]).unwrap();
assert_eq!(d.try_as_str().unwrap(), "pimatch");
}