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))