mlua/tests/luau.rs

200 lines
5.2 KiB
Rust
Raw Normal View History

2022-03-20 16:01:04 -04:00
#![cfg(feature = "luau")]
use std::env;
use std::fs;
2022-03-30 17:01:06 -04:00
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
2022-03-20 16:01:04 -04:00
2022-03-30 17:01:06 -04:00
use mlua::{Error, Lua, Result, Table, ThreadStatus, Value, VmState};
2022-03-20 16:01:04 -04:00
#[test]
fn test_require() -> Result<()> {
let lua = Lua::new();
let temp_dir = tempfile::tempdir().unwrap();
fs::write(
temp_dir.path().join("module.luau"),
r#"
counter = counter or 0
return counter + 1
"#,
)?;
env::set_var("LUAU_PATH", temp_dir.path().join("?.luau"));
lua.load(
r#"
local module = require("module")
assert(module == 1)
module = require("module")
assert(module == 1)
"#,
)
.exec()
}
2022-03-22 16:52:10 -04:00
#[test]
fn test_vectors() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
globals.set(
"vector",
lua.create_function(|_, (x, y, z)| Ok(Value::Vector(x, y, z)))?,
)?;
let v: [f32; 3] = lua
.load("return vector(1, 2, 3) + vector(3, 2, 1)")
.eval()?;
assert_eq!(v, [4.0, 4.0, 4.0]);
Ok(())
}
#[test]
fn test_readonly_table() -> Result<()> {
let lua = Lua::new();
let t = lua.create_table()?;
assert!(!t.is_readonly());
t.set_readonly(true);
assert!(t.is_readonly());
match t.set("key", "value") {
Err(Error::RuntimeError(err)) if err.contains("Attempt to modify a readonly table") => {}
r => panic!(
"expected RuntimeError(...) with a specific message, got {:?}",
r
),
};
Ok(())
}
2022-03-28 18:42:35 -04:00
#[test]
fn test_sandbox() -> Result<()> {
let lua = Lua::new();
lua.sandbox(true)?;
lua.load("global = 123").exec()?;
let n: i32 = lua.load("return global").eval()?;
assert_eq!(n, 123);
assert_eq!(lua.globals().get::<_, Option<i32>>("global")?, Some(123));
// Threads should inherit "main" globals
let f = lua.create_function(|lua, ()| lua.globals().get::<_, i32>("global"))?;
let co = lua.create_thread(f.clone())?;
assert_eq!(co.resume::<_, Option<i32>>(())?, Some(123));
// Sandboxed threads should also inherit "main" globals
let co = lua.create_thread(f)?;
co.sandbox()?;
assert_eq!(co.resume::<_, Option<i32>>(())?, Some(123));
lua.sandbox(false)?;
// Previously set variable `global` should be cleared now
assert_eq!(lua.globals().get::<_, Option<i32>>("global")?, None);
// Readonly flags should be cleared as well
let table = lua.globals().get::<_, Table>("table")?;
table.set("test", "test")?;
Ok(())
}
#[test]
fn test_sandbox_threads() -> Result<()> {
let lua = Lua::new();
let f = lua.create_function(|lua, v: Value| lua.globals().set("global", v))?;
let co = lua.create_thread(f.clone())?;
co.resume(321)?;
// The main state should see the `global` variable (as the thread is not sandboxed)
assert_eq!(lua.globals().get::<_, Option<i32>>("global")?, Some(321));
let co = lua.create_thread(f.clone())?;
co.sandbox()?;
co.resume(123)?;
// The main state should see the previous `global` value (as the thread is sandboxed)
assert_eq!(lua.globals().get::<_, Option<i32>>("global")?, Some(321));
// Try to reset the (sandboxed) thread
co.reset(f)?;
co.resume(111)?;
assert_eq!(lua.globals().get::<_, Option<i32>>("global")?, Some(111));
Ok(())
}
2022-03-30 17:01:06 -04:00
#[test]
fn test_interrupts() -> Result<()> {
let lua = Lua::new();
let interrupts_count = Arc::new(AtomicU64::new(0));
let interrupts_count2 = interrupts_count.clone();
lua.set_interrupt(move |_lua| {
interrupts_count2.fetch_add(1, Ordering::Relaxed);
Ok(VmState::Continue)
});
let f = lua
.load(
r#"
local x = 2 + 3
local y = x * 63
local z = string.len(x..", "..y)
"#,
)
.into_function()?;
f.call(())?;
assert!(interrupts_count.load(Ordering::Relaxed) > 0);
//
// Test yields from interrupt
//
let yield_count = Arc::new(AtomicU64::new(0));
let yield_count2 = yield_count.clone();
lua.set_interrupt(move |_lua| {
if yield_count2.fetch_add(1, Ordering::Relaxed) == 1 {
return Ok(VmState::Yield);
}
Ok(VmState::Continue)
});
let co = lua.create_thread(
lua.load(
r#"
local a = {1, 2, 3}
local b = 0
for _, x in ipairs(a) do b += x end
return b
"#,
)
.into_function()?,
)?;
co.resume(())?;
assert_eq!(co.status(), ThreadStatus::Resumable);
let result: i32 = co.resume(())?;
assert_eq!(result, 6);
assert_eq!(yield_count.load(Ordering::Relaxed), 7);
assert_eq!(co.status(), ThreadStatus::Unresumable);
//
// Test errors in interrupts
//
lua.set_interrupt(|_| Err(Error::RuntimeError("error from interrupt".into())));
match f.call::<_, ()>(()) {
Err(Error::CallbackError { cause, .. }) => match *cause {
Error::RuntimeError(ref m) if m == "error from interrupt" => {}
ref e => panic!("expected RuntimeError with a specific message, got {:?}", e),
},
r => panic!("expected CallbackError, got {:?}", r),
}
lua.remove_interrupt();
Ok(())
}