108 lines
2.7 KiB
Lua
108 lines
2.7 KiB
Lua
if (...) ~= nil and (...).type == "group" then return end -- prevent recursive harness call
|
||
|
||
local ok = pcall(require, "lb:task")
|
||
if not ok then error("lua test harness requires lb:task module") end
|
||
local ok, fs = pcall(require, "lb:fs")
|
||
if not ok then error("lua test harness requires lb:fs module") end
|
||
|
||
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()
|
||
local function glob(path, pat)
|
||
for entry in fs:glob_dir(path, pat) do
|
||
local path = entry:path()
|
||
local f, err = loadfile(path)
|
||
if not f then error(err) end
|
||
describe(path, f)
|
||
end
|
||
end
|
||
|
||
glob("tests", "**/*.lua")
|
||
glob("crates", "*/tests/**/*.lua")
|
||
end))
|