Files
test-samurai.nvim/lua/test-samurai/runners/go.lua

210 lines
4.5 KiB
Lua

local util = require("test-samurai.util")
local runner = {
name = "go",
}
local function find_block_end(lines, start_idx)
local depth = 0
local started = false
for i = start_idx, #lines do
local line = lines[i]
for j = 1, #line do
local ch = line:sub(j, j)
if ch == "{" then
depth = depth + 1
started = true
elseif ch == "}" then
if started then
depth = depth - 1
if depth == 0 then
return i - 1
end
end
end
end
end
return #lines - 1
end
local function find_test_functions(lines)
local funcs = {}
for i, line in ipairs(lines) do
local name = line:match("^%s*func%s+([%w_]+)%s*%(")
if not name then
name = line:match("^%s*func%s+%([^)]-%)%s+([%w_]+)%s*%(")
end
if name and line:find("%*testing%.T") then
local start_0 = i - 1
local end_0 = find_block_end(lines, i)
table.insert(funcs, {
name = name,
start = start_0,
["end"] = end_0,
})
end
end
return funcs
end
local function find_t_runs(lines, func)
local subtests = {}
for i = func.start + 1, func["end"] do
local line = lines[i + 1]
if line then
local name = line:match("t%.Run%(%s*['\"]([^'\"]+)['\"]")
if name then
local start_idx = i + 1
local end_0 = find_block_end(lines, start_idx)
table.insert(subtests, {
name = name,
start = start_idx - 1,
["end"] = end_0,
})
end
end
end
return subtests
end
local function build_run_pattern(spec)
local name = spec.test_path or ""
local escaped = name:gsub("(%W)", "%%%1")
if spec.scope == "function" then
return "^" .. escaped .. "$|^" .. escaped .. "/"
else
return "^" .. escaped .. "$"
end
end
local function build_pkg_arg(spec)
local file = spec.file
local cwd = spec.cwd
if not file or not cwd or file == "" or cwd == "" then
return "./..."
end
local dir = vim.fs.dirname(file)
if dir == cwd then
return "./"
end
if file:sub(1, #cwd) ~= cwd then
return "./..."
end
local rel = dir:sub(#cwd + 2)
if not rel or rel == "" then
return "./"
end
return "./" .. rel
end
function runner.is_test_file(bufnr)
local path = util.get_buf_path(bufnr)
if not path or path == "" then
return false
end
return path:sub(-8) == "_test.go"
end
function runner.find_nearest(bufnr, row, _col)
if not runner.is_test_file(bufnr) then
return nil, "not a Go test file"
end
local lines = util.get_buf_lines(bufnr)
local funcs = find_test_functions(lines)
local current
for _, f in ipairs(funcs) do
if row >= f.start and row <= f["end"] then
current = f
break
end
end
if not current then
return nil, "cursor not inside a test function"
end
local subtests = find_t_runs(lines, current)
local inside_sub
for _, sub in ipairs(subtests) do
if row >= sub.start and row <= sub["end"] then
inside_sub = sub
break
end
end
local path = util.get_buf_path(bufnr)
local root = util.find_root(path, { "go.mod", ".git" })
if inside_sub then
local full = current.name .. "/" .. inside_sub.name
return {
file = path,
cwd = root,
test_path = full,
scope = "subtest",
func = current.name,
subtest = inside_sub.name,
}
else
return {
file = path,
cwd = root,
test_path = current.name,
scope = "function",
func = current.name,
}
end
end
function runner.build_command(spec)
local pattern = build_run_pattern(spec)
local pkg = build_pkg_arg(spec)
local cmd = { "go", "test", "-v", pkg, "-run", pattern }
return {
cmd = cmd,
cwd = spec.cwd,
}
end
function runner.build_file_command(bufnr)
local path = util.get_buf_path(bufnr)
if not path or path == "" then
return nil
end
local root = util.find_root(path, { "go.mod", ".git" })
if not root or root == "" then
root = vim.loop.cwd()
end
local spec = { file = path, cwd = root }
local pkg = build_pkg_arg(spec)
local cmd = { "go", "test", "-v", pkg }
return {
cmd = cmd,
cwd = root,
}
end
function runner.build_all_command(bufnr)
local path = util.get_buf_path(bufnr)
local root
if path and path ~= "" then
root = util.find_root(path, { "go.mod", ".git" })
end
if not root or root == "" then
root = vim.loop.cwd()
end
local cmd = { "go", "test", "-v", "./..." }
return {
cmd = cmd,
cwd = root,
}
end
return runner