Skip to content

Commit

Permalink
feat: experimental gotestsum support and larger refactoring (#81)
Browse files Browse the repository at this point in the history
  • Loading branch information
fredrikaverpil authored Jul 3, 2024
1 parent d9b0bb2 commit 8672472
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 141 deletions.
89 changes: 89 additions & 0 deletions lua/neotest-golang/cmd.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
--- Helper functions building the command to execute.

local async = require("neotest.async")

local options = require("neotest-golang.options")
local json = require("neotest-golang.json")

local M = {}

function M.golist_output(cwd)
-- call 'go list -json ./...' to get test file data
local go_list_command = {
"go",
"list",
"-json",
"./...",
}
local output =
vim.fn.system("cd " .. cwd .. " && " .. table.concat(go_list_command, " "))
return json.process_golist_output(output)
end

function M.test_command_for_individual_test(
test_folder_absolute_path,
test_name
)
local go_test_required_args = { test_folder_absolute_path, "-run", test_name }
local cmd, json_filepath = M.test_command(go_test_required_args)
return cmd, json_filepath
end

function M.test_command_for_dir(module_name)
local go_test_required_args = { module_name }
local cmd, json_filepath = M.test_command(go_test_required_args)
return cmd, json_filepath
end

function M.test_command(go_test_required_args)
--- The runner to use for running tests.
--- @type string
local runner = M.fallback_to_go_test(options.get().runner)

--- The filepath to write test output JSON to, if using `gotestsum`.
--- @type string | nil
local json_filepath = nil

--- The final test command to execute.
--- @type table<string>
local cmd = {}

if runner == "go" then
cmd = M.go_test(go_test_required_args)
elseif runner == "gotestsum" then
json_filepath = vim.fs.normalize(async.fn.tempname())
cmd = M.gotestsum(go_test_required_args, json_filepath)
end

return cmd, json_filepath
end

function M.go_test(go_test_required_args)
local cmd = { "go", "test", "-json" }
cmd = vim.list_extend(vim.deepcopy(cmd), options.get().go_test_args)
cmd = vim.list_extend(vim.deepcopy(cmd), go_test_required_args)
return cmd
end

function M.gotestsum(go_test_required_args, json_filepath)
local cmd = { "gotestsum", "--jsonfile=" .. json_filepath }
cmd = vim.list_extend(vim.deepcopy(cmd), options.get().gotestsum_args)
cmd = vim.list_extend(vim.deepcopy(cmd), { "--" })
cmd = vim.list_extend(vim.deepcopy(cmd), options.get().go_test_args)
cmd = vim.list_extend(vim.deepcopy(cmd), go_test_required_args)
return cmd
end

function M.fallback_to_go_test(executable)
if vim.fn.executable(executable) == 0 then
vim.notify(
"Runner " .. executable .. " not found. Falling back to 'go'.",
vim.log.levels.WARN
)
options.set({ runner = "go" })
return options.get().runner
end
return options.get().runner
end

return M
38 changes: 38 additions & 0 deletions lua/neotest-golang/dap.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
--- DAP setup related functions.

local options = require("neotest-golang.options")

local M = {}

function M.setup_debugging(cwd)
local dap_go_opts = options.get().dap_go_opts or {}
local dap_go_opts_original = vim.deepcopy(dap_go_opts)
if dap_go_opts.delve == nil then
dap_go_opts.delve = {}
end
dap_go_opts.delve.cwd = cwd
require("dap-go").setup(dap_go_opts)

-- reset nvim-dap-go (and cwd) after debugging with nvim-dap
require("dap").listeners.after.event_terminated["neotest-golang-debug"] = function()
require("dap-go").setup(dap_go_opts_original)
end
end

--- @param test_name string
--- @return table | nil
function M.get_dap_config(test_name)
-- :help dap-configuration
local dap_config = {
type = "go",
name = "Neotest-golang",
request = "launch",
mode = "test",
program = "${fileDirname}",
args = { "-test.run", "^" .. test_name .. "$" },
}

return dap_config
end

return M
8 changes: 5 additions & 3 deletions lua/neotest-golang/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,13 @@ function M.Adapter.results(spec, result, tree)
if spec.context.pos_type == "dir" then
-- A test command executed a directory of tests and the output/status must
-- now be processed.
local results = parse.results(spec, result, tree)
local results = parse.test_results(spec, result, tree)
M.workaround_neotest_issue_391(result)
return results
elseif spec.context.pos_type == "test" then
-- A test command executed a single test and the output/status must now be
-- processed.
local results = parse.results(spec, result, tree)
local results = parse.test_results(spec, result, tree)
M.workaround_neotest_issue_391(result)
return results
end
Expand All @@ -171,7 +171,9 @@ function M.workaround_neotest_issue_391(result)
-- FIXME: once output is parsed, erase file contents, so to avoid JSON in
-- output panel. This is a workaround for now, only because of
-- https://github.com/nvim-neotest/neotest/issues/391
vim.fn.writefile({ "" }, result.output)
if result.output ~= nil then
vim.fn.writefile({ "" }, result.output)
end
end

--- Adapter options.
Expand Down
15 changes: 10 additions & 5 deletions lua/neotest-golang/options.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@
local M = {}

local opts = {
go_test_args = {
"-v",
"-race",
"-count=1",
},
runner = "go", -- or "gotestsum"
go_test_args = { "-v", "-race", "-count=1" },
gotestsum_args = { "--format=standard-verbose" },
dap_go_enabled = false,
dap_go_opts = {},
warn_test_name_dupes = true,
Expand All @@ -30,4 +28,11 @@ function M.get()
return opts
end

function M.set(updated_opts)
for k, v in pairs(updated_opts) do
opts[k] = v
end
return opts
end

return M
22 changes: 14 additions & 8 deletions lua/neotest-golang/parse.lua
Original file line number Diff line number Diff line change
Expand Up @@ -27,28 +27,34 @@ local M = {}
--- @param result neotest.StrategyResult
--- @param tree neotest.Tree
--- @return table<string, neotest.Result>
function M.results(spec, result, tree)
if spec.context.skip == true then
function M.test_results(spec, result, tree)
if spec.context.debug_and_skip == true then
---@type table<string, neotest.Result>
local results = {}
results[spec.context.id] = {
---@type neotest.ResultStatus
status = "skipped", -- default value
}

return results
end

--- The Neotest position tree node for this execution.
--- @type neotest.Position
local pos = tree:data()

--- The raw output from the 'go test -json' command.
--- @type table
local raw_output = async.fn.readfile(result.output)
--- The runner to use for running tests.
--- @type string
local runner = options.get().runner

--- The 'go test' JSON output, converted into a lua table.
--- The raw output from the test command.
--- @type table
local raw_output = {}
if runner == "go" then
raw_output = async.fn.readfile(result.output)
elseif runner == "gotestsum" then
raw_output = async.fn.readfile(spec.context.json_filepath)
end

local gotest_output = json.process_gotest_output(raw_output)

--- The 'go list -json' output, converted into a lua table.
Expand Down Expand Up @@ -86,7 +92,7 @@ function M.results(spec, result, tree)
}

-- if the test execution was skipped, return early
if spec.context.skip == true then
if spec.context.test_execution_skipped == true then
return neotest_result
end

Expand Down
82 changes: 22 additions & 60 deletions lua/neotest-golang/runspec_dir.lua
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
--- Helpers to build the command and context around running all tests of
--- a Go module.

local options = require("neotest-golang.options")
local json = require("neotest-golang.json")
local cmd = require("neotest-golang.cmd")

local M = {}

Expand All @@ -17,10 +16,6 @@ local M = {}
function M.build(pos)
local go_mod_filepath = M.find_file_upwards("go.mod", pos.path)

-- if go_mod_filepath == nil then
-- go_mod_filepath = M.find_file_upwards("go.work", pos.path)
-- end

-- if no go.mod file was found up the directory tree, until reaching $CWD,
-- then we cannot determine the Go project root.
if go_mod_filepath == nil then
Expand All @@ -31,27 +26,15 @@ function M.build(pos)
command = { "echo", msg },
context = {
id = pos.id,
skip = true,
test_execution_skipped = true,
pos_type = "dir",
},
}
return run_spec
end

local go_mod_folderpath = vim.fn.fnamemodify(go_mod_filepath, ":h")
local cwd = go_mod_folderpath

-- call 'go list -json ./...' to get test file data
local go_list_command = {
"go",
"list",
"-json",
"./...",
}
local go_list_command_result = vim.fn.system(
"cd " .. go_mod_folderpath .. " && " .. table.concat(go_list_command, " ")
)
local golist_output = json.process_golist_output(go_list_command_result)
local golist_output = cmd.golist_output(go_mod_folderpath)

-- find the go module that corresponds to the go_mod_folderpath
local module_name = "./..." -- if no go module, run all tests at the $CWD
Expand All @@ -62,7 +45,25 @@ function M.build(pos)
end
end

return M.build_dir_test_runspec(pos, cwd, golist_output, module_name)
local test_cmd, json_filepath = cmd.test_command_for_dir(module_name)

--- @type neotest.RunSpec
local run_spec = {
command = test_cmd,
cwd = go_mod_folderpath,
context = {
id = pos.id,
test_filepath = pos.path,
golist_output = golist_output,
pos_type = "dir",
},
}

if json_filepath ~= nil then
run_spec.context.json_filepath = json_filepath
end

return run_spec
end

--- Find a file upwards in the directory tree and return its path, if found.
Expand Down Expand Up @@ -107,43 +108,4 @@ function M.remove_base_path(base_path, target_path)
return target_path
end

--- Build runspec for a directory of tests
--- @param pos neotest.Position
--- @param cwd string
--- @param golist_output table
--- @param module_name string
--- @return neotest.RunSpec | neotest.RunSpec[] | nil
function M.build_dir_test_runspec(pos, cwd, golist_output, module_name)
local gotest = {
"go",
"test",
"-json",
}

--- @type table
local required_go_test_args = {
module_name,
}

local combined_args = vim.list_extend(
vim.deepcopy(options.get().go_test_args),
required_go_test_args
)
local gotest_command = vim.list_extend(vim.deepcopy(gotest), combined_args)

--- @type neotest.RunSpec
local run_spec = {
command = gotest_command,
cwd = cwd,
context = {
id = pos.id,
test_filepath = pos.path,
golist_output = golist_output,
pos_type = "dir",
},
}

return run_spec
end

return M
2 changes: 1 addition & 1 deletion lua/neotest-golang/runspec_file.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function M.build(pos, tree)
command = { "echo", "No tests found in file" },
context = {
id = pos.id,
skip = true,
test_execution_skipped = true,
pos_type = "test", -- TODO: to be implemented as "file" later
},
}
Expand Down
Loading

0 comments on commit 8672472

Please sign in to comment.