Implement basic lua test harness

This commit is contained in:
2025-06-27 03:11:49 +10:00
parent db4faea821
commit 40829cdfc6
5 changed files with 157 additions and 11 deletions

97
tests/main.lua Normal file
View File

@@ -0,0 +1,97 @@
if (...) ~= nil and (...).type == "group" then return end -- prevent recursive main call
local fs = require("lb:fs")
local global = _G
local colors = {
reset = "\x1b[0m",
pass = "\x1b[32;1m",
fail = "\x1b[31;1m",
}
local function color(name, s)
return colors[name] .. s .. colors.reset
end
local function create_test(name, f, group)
local test = { type = "test", name = name or "", group = group, state = "pending", f = f }
local fenv = setmetatable({}, { __index = global })
setfenv(f, fenv)
return test
end
local function create_group(name, f, parent)
local group = { type = "group", name = name or "", parent = parent, items = {} }
local fenv = setmetatable({
describe = function(name, f)
local item = create_group(name, f, group)
table.insert(group.items, item)
return item
end,
test = function(name, f)
local item = create_test(name, f, group)
table.insert(group.items, item)
return item
end,
}, { __index = global })
setfenv(f, fenv)
f(group)
return group
end
local function name_test(test)
local name = test.name
local group = test.group
while group ~= nil do
if group.name ~= "" then name = group.name .. " " .. name end
group = group.parent
end
return name
end
local function trace(msg)
return color("fail", msg) .. debug.traceback("", 2):sub(("\nstack traceback:"):len() + 1)
end
local function run_test(test)
local ok, res = xpcall(test.f, trace, test)
if ok then
test.state = "pass"
print("", color("pass", "PASS") .. " " .. name_test(test))
else
test.state = "fail"
print("", color("fail", "FAIL") .. " " .. name_test(test) .. "\n")
print(res .. "\n")
end
return test
end
local function start(cx, item)
if item.type == "test" then
table.insert(cx.tasks, spawn(run_test, item))
elseif item.type == "group" then
for _, item in ipairs(item.items) do
start(cx, item)
end
end
end
local function run(item)
local cx = { tasks = {} }
local pass = true
start(cx, item)
for _, task in ipairs(cx.tasks) do
if task:await().state ~= "pass" then pass = false end
end
if pass then return 0 end -- report status to cargo
return 1
end
return run(create_group("", function()
for entry in fs:glob("{tests,crates/*/tests}/**/*.lua") do
local path = entry:path():sub(3)
local f, err = loadfile(path)
if not f then error(err) end
describe(path, f)
end
end))

44
tests/main.rs Normal file
View File

@@ -0,0 +1,44 @@
use luajit::Chunk;
use owo_colors::OwoColorize;
use std::{
fs,
process::{self, ExitCode},
};
fn main() -> ExitCode {
let tokio = tokio::runtime::Runtime::new().unwrap();
let lua = {
let mut rt = lb::runtime::Builder::new();
luby::open(&mut rt);
rt.unhandled_error(error_cb).build().unwrap()
};
let path = "tests/main.lua";
let main = lua.spawn(async move |s| {
if let Err(ref err) = s.load(Chunk::new(fs::read(path).unwrap()).path(path)) {
s.report_error(err);
} else if let Err(ref err) = s.call_async(0, 1).await {
s.report_error(err);
}
if s.slot(1).integer().unwrap_or(1) == 0 {
ExitCode::SUCCESS
} else {
ExitCode::FAILURE
}
});
tokio.block_on(async move {
lua.await;
main.await.unwrap()
})
}
fn error_cb(err: &luajit::Error) {
match err.trace() {
Some(trace) => eprintln!("{}\n{trace}", err.red().bold()),
None => eprintln!("{}", err.red().bold()),
}
process::exit(1);
}