Files
nvim/test/init.lua
T

165 lines
3.9 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)
local parent = vim.fs.dirname(full)
vim.fn.mkdir(parent, "p")
local f, err = io.open(full, "w")
if not f then
error(err or "io.open failed: " .. full)
end
f:write(content)
f:close()
end
---@param keys string
function M.press(keys)
local rhs = vim.api.nvim_replace_termcodes(keys, true, false, true)
vim.api.nvim_feedkeys(rhs, "x", false)
end
---@param cond fun(): boolean
---@param msg string
---@param timeout integer?
function M.wait_for(cond, msg, timeout)
M.truthy(vim.wait(timeout or 1000, cond), "timed out waiting for: " .. msg)
end
return M