rlua-searcher/tests/tests.rs
2021-06-15 07:28:46 +10:00

333 lines
9.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use rlua::{Context, Function, Lua, Table, UserData, UserDataMethods, Value};
use rlua_searcher::{AddSearcher, Result};
use std::borrow::Cow;
use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
#[test]
fn add_searcher_works() {
// These should end up in the same hash slot.
let name_owned = Cow::from("lume".to_string());
let name_ref = Cow::from("lume");
// `lume_ref` should overwrite `lume_owned`.
let lume_owned = Cow::from(read_lume_to_string());
let lume_ref = Cow::from(read_lume_to_str());
let mut map = HashMap::new();
map.insert(name_owned, lume_owned);
map.insert(name_ref, lume_ref);
let lua = Lua::new();
let hello = lua
.context::<_, Result<String>>(|lua_ctx| {
lua_ctx.add_searcher(map)?;
Ok(lua_ctx.load(r#"return require("lume")"#).eval()?)
})
.unwrap();
assert_eq!("hello ref", hello);
// Repeat the experiment, but with an additional overwrite.
let name_owned = Cow::from("lume".to_string());
let name_ref = Cow::from("lume");
let lume_owned = Cow::from(read_lume_to_string());
let lume_ref = Cow::from(read_lume_to_str());
let mut map = HashMap::new();
map.insert(name_owned, lume_owned);
map.insert(name_ref, lume_ref);
let name_owned = Cow::from("lume".to_string());
let lume_owned = Cow::from(read_lume_to_string());
map.insert(name_owned, lume_owned);
let lua = Lua::new();
let hello = lua
.context::<_, Result<String>>(|lua_ctx| {
lua_ctx.add_searcher(map)?;
Ok(lua_ctx.load(r#"return require("lume")"#).eval()?)
})
.unwrap();
assert_eq!("hello owned", hello);
}
#[test]
fn add_path_searcher_works() {
let name = Cow::from("lume".to_string());
let path = PathBuf::new()
.join(std::env::var("CARGO_MANIFEST_DIR").unwrap())
.join("tests")
.join("data")
.join("lume.lua");
let mut map = HashMap::new();
map.insert(name, path);
let lua = Lua::new();
let hello = lua
.context::<_, Result<String>>(|lua_ctx| {
lua_ctx.add_path_searcher(map)?;
Ok(lua_ctx.load(r#"return require("lume")"#).eval()?)
})
.unwrap();
assert_eq!("hello lume", hello);
}
#[test]
fn module_reloading_works() {
let name = Cow::from("lume".to_string());
let path = PathBuf::new()
.join(std::env::var("CARGO_MANIFEST_DIR").unwrap())
.join("tests")
.join("data")
.join("lume.lua");
let mut map = HashMap::new();
map.insert(name.clone(), path.clone());
let lua = Lua::new();
// Add searcher for lume module on disk, and read from it.
let hello = lua
.context::<_, Result<String>>(|lua_ctx| {
lua_ctx.add_path_searcher(map)?;
Ok(lua_ctx.load(r#"return require("lume")"#).eval()?)
})
.unwrap();
assert_eq!("hello lume", hello);
// Twice.
let hello = lua
.context::<_, Result<String>>(|lua_ctx| {
Ok(lua_ctx.load(r#"return require("lume")"#).eval()?)
})
.unwrap();
assert_eq!("hello lume", hello);
// Modify lume module on disk.
let mut out = File::create(path.clone()).expect("Could not create Lume module on disk");
write!(out, "{}\n", r#"return "hello again lume""#)
.expect("Could not modify Lume module on disk");
// Thrice. Should still be unchanged due to caching.
let hello = lua
.context::<_, Result<String>>(|lua_ctx| {
Ok(lua_ctx.load(r#"return require("lume")"#).eval()?)
})
.unwrap();
assert_eq!("hello lume", hello);
// Remove lume module from Luas `package.loaded` cache to facilitate reload.
lua.context::<_, rlua::Result<()>>(|lua_ctx| {
let globals = lua_ctx.globals();
let loaded: Table = globals.get::<_, Table>("package")?.get("loaded")?;
loaded.set(name.as_ref(), Value::Nil)
})
.unwrap();
// Re-read from lume module on disk.
let hello = lua
.context::<_, Result<String>>(|lua_ctx| {
Ok(lua_ctx.load(r#"return require("lume")"#).eval()?)
})
.unwrap();
assert_eq!("hello again lume", hello);
// Twice.
let hello = lua
.context::<_, Result<String>>(|lua_ctx| {
Ok(lua_ctx.load(r#"return require("lume")"#).eval()?)
})
.unwrap();
assert_eq!("hello again lume", hello);
// Revert changes to lume module on disk.
let mut out = File::create(path).expect("Could not create Lume module on disk");
write!(out, "{}\n", r#"return "hello lume""#).expect("Could not modify Lume module on disk");
// Clear cache again.
lua.context::<_, rlua::Result<()>>(|lua_ctx| {
let globals = lua_ctx.globals();
let loaded: Table = globals.get::<_, Table>("package")?.get("loaded")?;
loaded.set(name.as_ref(), Value::Nil)
})
.unwrap();
// Ensure changes have been successfully reverted.
let hello = lua
.context::<_, Result<String>>(|lua_ctx| {
Ok(lua_ctx.load(r#"return require("lume")"#).eval()?)
})
.unwrap();
assert_eq!("hello lume", hello);
}
fn read_lume_to_string() -> String {
r#"return "hello owned""#.to_string()
}
fn read_lume_to_str() -> &'static str {
r#"return "hello ref""#
}
#[test]
fn add_closure_searcher_works() {
let lua = Lua::new();
let mut modules: HashMap<
Cow<'static, str>,
Box<
dyn for<'ctx> Fn(Context<'ctx>, Table<'ctx>, &str) -> rlua::Result<Function<'ctx>>
+ Send,
>,
> = HashMap::new();
let instrument_loader: Box<
dyn for<'ctx> Fn(Context<'ctx>, Table<'ctx>, &str) -> rlua::Result<Function<'ctx>> + Send,
> = Box::new(|lua_ctx, env, name| {
let globals = lua_ctx.globals();
let new = lua_ctx.create_function(|_, (name, sound): (String, String)| {
Ok(Instrument::new(name, sound))
})?;
let tbl = lua_ctx.create_table()?;
tbl.set("new", new)?;
globals.set("instrument", tbl)?;
Ok(lua_ctx
.load("return instrument")
.set_name(name)?
.set_environment(env)?
.into_function()?)
});
modules.insert(Cow::from("instrument".to_string()), instrument_loader);
let sound = lua
.context::<_, Result<String>>(|lua_ctx| {
lua_ctx.add_closure_searcher(modules)?;
// Ensure global variable `instrument` is unset.
let nil: String = lua_ctx.load("return type(instrument)").eval()?;
assert_eq!(nil, "nil");
Ok(lua_ctx
.load(
r#"local instrument = require("instrument")
local ukulele = instrument.new("ukulele", "twang")
return ukulele:play()"#,
)
.eval()?)
})
.unwrap();
assert_eq!(sound, "The ukulele goes twang");
}
struct Instrument {
name: String,
sound: String,
}
impl Instrument {
pub fn new(name: String, sound: String) -> Self {
Self { name, sound }
}
pub fn play(&self) -> String {
format!("The {} goes {}", self.name, self.sound)
}
}
impl UserData for Instrument {
fn add_methods<'lua, M>(methods: &mut M)
where
M: UserDataMethods<'lua, Self>,
{
methods.add_method("play", |_, instrument, ()| Ok(instrument.play()));
}
}
#[test]
fn add_function_searcher_works() {
let lua = Lua::new();
let mut modules: HashMap<
Cow<'static, str>,
for<'ctx> fn(Context<'ctx>, Table<'ctx>, &str) -> rlua::Result<Function<'ctx>>,
> = HashMap::new();
modules.insert(Cow::from("cartridge".to_string()), cartridge_loader);
let title = lua
.context::<_, Result<String>>(|lua_ctx| {
lua_ctx.add_function_searcher(modules)?;
// Ensure global variable `cartridge` is unset.
let nil: String = lua_ctx.load("return type(cartridge)").eval()?;
assert_eq!(nil, "nil");
Ok(lua_ctx
.load(
r#"local cartridge = require("cartridge")
local smash = cartridge.pick()
return smash:play()"#,
)
.eval()?)
})
.unwrap();
assert_eq!(title, "Super Smash Brothers 64");
}
struct Cartridge {
title: String,
}
impl Cartridge {
pub fn pick() -> Self {
let title = "Super Smash Brothers 64".to_string();
Self { title }
}
pub fn play(&self) -> String {
self.title.clone()
}
}
impl UserData for Cartridge {
fn add_methods<'lua, M>(methods: &mut M)
where
M: UserDataMethods<'lua, Self>,
{
methods.add_method("play", |_, cartridge, ()| Ok(cartridge.play()));
}
}
fn cartridge_loader<'ctx>(
lua_ctx: Context<'ctx>,
env: Table<'ctx>,
name: &str,
) -> rlua::Result<Function<'ctx>> {
let globals = lua_ctx.globals();
let pick = lua_ctx.create_function(|_, ()| Ok(Cartridge::pick()))?;
let tbl = lua_ctx.create_table()?;
tbl.set("pick", pick)?;
globals.set("cartridge", tbl)?;
Ok(lua_ctx
.load("return cartridge")
.set_name(name)?
.set_environment(env)?
.into_function()?)
}