use std::path::{Path, PathBuf};
use std::process::Command;
const FIXTURES: &str = "tests/runtime/fixtures";
#[test]
fn generated_code_behaves_under_vide() {
std::thread::Builder::new()
.stack_size(16 * 1024 * 1024)
.spawn(run)
.expect("spawn")
.join()
.expect("golden runtime thread");
}
fn run() {
let (Some(vide), Some(lune)) = (env_path("LUAUX_VIDE"), env_path("LUAUX_LUNE")) else {
eprintln!(
"skipped: set LUAUX_VIDE to a Vide checkout and LUAUX_LUNE to a lune binary\n\
\x20 e.g. LUAUX_VIDE=../vide LUAUX_LUNE=$(which lune) cargo test --test runtime"
);
return;
};
let workspace = std::env::temp_dir().join("luaux-runtime-golden");
let _ = std::fs::remove_dir_all(&workspace);
std::fs::create_dir_all(workspace.join("fixtures")).expect("create workspace");
copy_tree(&vide.join("src"), &workspace.join("vide")).expect("copy vide/src");
copy_tree(&vide.join("test"), &workspace.join("test")).expect("copy vide/test");
let mut names = Vec::new();
for fixture in fixtures() {
let name = fixture
.file_stem()
.expect("stem")
.to_string_lossy()
.to_string();
let source = std::fs::read_to_string(&fixture).expect("read fixture");
let compiled = luaux::compile_verified(&source, &luaux::Vide, &luaux::Config::default())
.map(|(output, _)| output)
.unwrap_or_else(|error| panic!("{}: {error}", fixture.display()));
assert_eq!(
compiled.lines().count(),
source.lines().count(),
"{}: line count changed",
fixture.display()
);
std::fs::write(
workspace.join("fixtures").join(format!("{name}.luau")),
compiled,
)
.expect("write compiled fixture");
names.push(name);
}
assert!(!names.is_empty(), "no fixtures found under {FIXTURES}");
names.sort();
std::fs::write(workspace.join("run.luau"), runner(&names)).expect("write runner");
let output = Command::new(&lune)
.arg("run")
.arg("run.luau")
.current_dir(&workspace)
.output()
.expect("run lune");
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
eprintln!("{stdout}");
assert!(
output.status.success(),
"fixtures failed under Vide\n--- stdout ---\n{stdout}\n--- stderr ---\n{stderr}"
);
}
fn runner(names: &[String]) -> String {
let requires = names
.iter()
.map(|name| format!("\t{{ \"{name}\", require(\"./fixtures/{name}\") }},"))
.collect::<Vec<_>>()
.join("\n");
format!(
r#"local roblox = require("@lune/roblox")
-- Vide creates real Instances; Lune provides them, minus a few methods.
_G.Instance = roblox.Instance
_G.Color3 = roblox.Color3
_G.UDim2 = roblox.UDim2
_G.UDim = roblox.UDim
local vide = require("./vide/lib")
local failures = 0
local checks = 0
local fixtures = {{
{requires}
}}
for _, entry in fixtures do
local name, fixture = entry[1], entry[2]
vide.root(function()
fixture(function(label, ok)
checks += 1
if not ok then
failures += 1
print(` FAIL {{name}}: {{label}}`)
end
end)
end)
end
print(`{{checks}} check(s), {{failures}} failure(s)`)
if failures > 0 then
process.exit(1)
end
"#
)
}
fn fixtures() -> Vec<PathBuf> {
let mut found: Vec<PathBuf> = std::fs::read_dir(FIXTURES)
.expect("read fixtures directory")
.filter_map(|entry| entry.ok().map(|entry| entry.path()))
.filter(|path| path.extension().and_then(|ext| ext.to_str()) == Some("luaux"))
.collect();
found.sort();
found
}
fn env_path(key: &str) -> Option<PathBuf> {
let value = std::env::var(key).ok()?;
(!value.is_empty()).then(|| PathBuf::from(value))
}
fn copy_tree(from: &Path, to: &Path) -> std::io::Result<()> {
std::fs::create_dir_all(to)?;
for entry in std::fs::read_dir(from)? {
let entry = entry?;
let source = entry.path();
let destination = to.join(entry.file_name());
if source.is_dir() {
copy_tree(&source, &destination)?;
} else {
std::fs::copy(&source, &destination)?;
}
}
Ok(())
}