148 lines
3.4 KiB
Lua
148 lines
3.4 KiB
Lua
local M = {}
|
|
|
|
local stats = { passed = 0, failed = 0, errors = {} }
|
|
local defers = {}
|
|
local label = "?"
|
|
local started = false
|
|
|
|
local color_on = vim.env.TEST_COLOR == "1"
|
|
|
|
local function color(code, str)
|
|
if color_on then
|
|
return string.format("\27[%sm%s\27[0m", code, str)
|
|
end
|
|
return str
|
|
end
|
|
local function red(s)
|
|
return color("31", s)
|
|
end
|
|
local function green(s)
|
|
return color("32", s)
|
|
end
|
|
|
|
local function ensure_started()
|
|
if started then
|
|
return
|
|
end
|
|
started = true
|
|
io.stdout:write(string.format("-> %s ", label))
|
|
io.stdout:flush()
|
|
end
|
|
|
|
---Begin a new test file. Resets per-file stats and the cleanup queue
|
|
---and stages the per-file header for the next `M.test` call.
|
|
---@param path string
|
|
function M.start_file(path)
|
|
label = path
|
|
started = false
|
|
stats = { passed = 0, failed = 0, errors = {} }
|
|
defers = {}
|
|
end
|
|
|
|
---Print the per-file summary and return the counts so the runner can
|
|
---accumulate totals across files.
|
|
---@return integer passed
|
|
---@return integer failed
|
|
function M.report()
|
|
ensure_started()
|
|
if stats.failed > 0 then
|
|
io.stdout:write(" " .. red("FAIL") .. "\n")
|
|
for _, e in ipairs(stats.errors) do
|
|
io.stdout:write(
|
|
string.format(
|
|
" %s %s\n %s\n",
|
|
red("FAIL"),
|
|
e.name,
|
|
e.err
|
|
)
|
|
)
|
|
end
|
|
else
|
|
io.stdout:write(" " .. green("OK") .. "\n")
|
|
end
|
|
return stats.passed, stats.failed
|
|
end
|
|
|
|
---Queue a function to run when the current `M.test` completes (whether
|
|
---it passed or failed). Deferred calls run in LIFO order.
|
|
---@param fn fun()
|
|
function M.defer(fn)
|
|
table.insert(defers, fn)
|
|
end
|
|
|
|
---@param name string
|
|
---@param fn fun()
|
|
function M.test(name, fn)
|
|
ensure_started()
|
|
local saved_cwd = vim.fn.getcwd()
|
|
local ok, err = pcall(fn)
|
|
while #defers > 0 do
|
|
pcall(table.remove(defers))
|
|
end
|
|
if vim.fn.getcwd() ~= saved_cwd then
|
|
pcall(vim.cmd.cd, saved_cwd)
|
|
end
|
|
if ok then
|
|
stats.passed = stats.passed + 1
|
|
io.stdout:write(".")
|
|
else
|
|
stats.failed = stats.failed + 1
|
|
table.insert(stats.errors, { name = name, err = err })
|
|
io.stdout:write(red("F"))
|
|
end
|
|
io.stdout:flush()
|
|
end
|
|
|
|
local function fmt_value(v)
|
|
if type(v) == "string" then
|
|
return string.format("%q", v)
|
|
end
|
|
return vim.inspect(v)
|
|
end
|
|
|
|
---@param actual any
|
|
---@param expected any
|
|
---@param msg string?
|
|
function M.eq(actual, expected, msg)
|
|
if not vim.deep_equal(actual, expected) then
|
|
error(
|
|
string.format(
|
|
"%s\n expected: %s\n actual: %s",
|
|
msg or "values differ",
|
|
fmt_value(expected),
|
|
fmt_value(actual)
|
|
),
|
|
2
|
|
)
|
|
end
|
|
end
|
|
|
|
---@param val any
|
|
---@param msg string?
|
|
function M.truthy(val, msg)
|
|
if not val then
|
|
error(msg or ("expected truthy, got " .. tostring(val)), 2)
|
|
end
|
|
end
|
|
|
|
---@param val any
|
|
---@param msg string?
|
|
function M.falsy(val, msg)
|
|
if val then
|
|
error(msg or ("expected falsy, got " .. tostring(val)), 2)
|
|
end
|
|
end
|
|
|
|
---@param dir string
|
|
---@param path string
|
|
---@param content string
|
|
function M.write(dir, path, content)
|
|
local full = vim.fs.joinpath(dir, path)
|
|
vim.fn.mkdir(vim.fs.dirname(full), "p")
|
|
local f = assert(io.open(full --[[@as string]], "w"))
|
|
f:write(content)
|
|
f:close()
|
|
end
|
|
|
|
return M
|