-- run `go test` local M = {} local utils = require("go.utils") local log = utils.log local empty = utils.empty local ginkgo = require("go.ginkgo") local getopt = require("go.alt_getopt") local long_opts = { verbose = "v", compile = "c", tags = "t", bench = "b", select = "s", floaterm = "F", } local sep = require("go.utils").sep() local short_opts = "vct:bsF" local bench_opts = { "-benchmem", "-cpuprofile", "profile.out" } M.efm = function() local indent = [[%\\%( %\\)]] local efm = [[%-G=== RUN %.%#]] efm = efm .. [[,%-G" .. indent .. "%#--- PASS: %.%#]] efm = efm .. [[,%G--- FAIL: %\\%(Example%\\)%\\@=%m (%.%#)]] efm = efm .. [[,%G" .. indent .. "%#--- FAIL: %m (%.%#)]] efm = efm .. [[,%A" .. indent .. "%\\+%[%^:]%\\+: %f:%l: %m]] efm = efm .. [[,%+Gpanic: test timed out after %.%\\+]] efm = efm .. ",%+Afatal error: %.%# [recovered]" efm = efm .. [[,%+Afatal error: %.%#]] efm = efm .. [[,%+Apanic: %.%#]] -- exit efm = efm .. ",%-Cexit status %[0-9]%\\+" efm = efm .. ",exit status %[0-9]%\\+" -- failed lines efm = efm .. ",%-CFAIL%\\t%.%#" -- compiling error efm = efm .. ",%A%f:%l:%c: %m" efm = efm .. ",%A%f:%l: %m" efm = efm .. ",%-C%.%#" efm = efm .. ",%-G%.%#" efm = string.gsub(efm, " ", [[\ ]]) log(efm) return efm end local function get_build_tags(args) -- local tags = "-tags" local tags local space = [[\ ]] if _GO_NVIM_CFG.run_in_floaterm then space = " " end if _GO_NVIM_CFG.build_tags ~= "" then tags = "-tags=" .. _GO_NVIM_CFG.build_tags end local optarg, optind, reminder = getopt.get_opts(args, short_opts, long_opts) if optarg["t"] then if tags then tags = tags .. space .. optarg["t"] else tags = "-tags=" .. optarg["t"] end end if tags then return { tags }, reminder end end M.get_build_tags = get_build_tags local function get_test_filebufnr() local fn = vim.fn.expand("%") log(fn) local bufnr = vim.api.nvim_get_current_buf() if not fn:find("test%.go$") then fn = require("go.alternate").alternate() fn = vim.fn.fnamemodify(fn, ":p") -- expand to full path local uri = vim.uri_from_fname(fn) bufnr = vim.uri_to_bufnr(uri) log(fn, bufnr, uri) if not vim.api.nvim_buf_is_loaded(bufnr) then vim.fn.bufload(bufnr) end end return bufnr end local function run_test(path, args) log(args) local compile = false local bench = false local optarg, optind, reminder = getopt.get_opts(args, short_opts, long_opts) if optarg["c"] then path = utils.rel_path() -- vim.fn.expand("%:p:h") can not resolve releative path compile = true end if optarg["b"] then bench = true end if next(reminder) then path = reminder[1] end local test_runner = _GO_NVIM_CFG.go if _GO_NVIM_CFG.test_runner ~= test_runner then test_runner = _GO_NVIM_CFG.test_runner require("go.install").install(test_runner) end local tags = get_build_tags(args) log(tags) local cmd = {} local run_in_floaterm = optarg["F"] or _GO_NVIM_CFG.run_in_floaterm if run_in_floaterm then table.insert(cmd, test_runner or "go") table.insert(cmd, "test") end if _GO_NVIM_CFG.verbose_tests then table.insert(cmd, "-v") end if not empty(tags) then cmd = vim.list_extend(cmd, tags) end if not empty(reminder) then cmd = vim.list_extend(cmd, reminder) end if compile == true then if path ~= "" then table.insert(cmd, path) end elseif bench == true then if path ~= "" then table.insert(cmd, "-bench=" .. path) else table.insert(cmd, "-bench=.") end vim.list_extend(cmd, bench_opts) else if path ~= "" then table.insert(cmd, path) else local argsstr = "." .. utils.sep() .. "..." cmd = table.insert(cmd, argsstr) end end utils.log(cmd, args) if run_in_floaterm then local term = require("go.term").run term({ cmd = cmd, autoclose = false }) return cmd end vim.cmd([[setl makeprg=]] .. _GO_NVIM_CFG.go .. [[\ test]]) utils.log("test cmd", cmd) return require("go.asyncmake").make(unpack(cmd)) end M.test = function(...) local args = { ... } log(args) vim.fn.setqflist({}) local workfolder = utils.work_path() if workfolder == nil then workfolder = "." end local fpath = workfolder .. utils.sep() .. "..." utils.log("fpath :" .. fpath) run_test(fpath, args) end M.test_suit = function(...) local args = { ... } log(args) local workfolder = utils.work_path() utils.log(args) local fpath = workfolder .. utils.sep() .. "..." utils.log("fpath" .. fpath) run_test(fpath, args) end M.test_package = function(...) local args = { ... } log(args) local fpath = "." .. sep .. vim.fn.fnamemodify(vim.fn.expand("%:h"), ":~:.") .. sep .. "..." utils.log("fpath: " .. fpath) -- args[#args + 1] = fpath return run_test(fpath, args) end M.get_test_func_name = function() local row, col = unpack(vim.api.nvim_win_get_cursor(0)) row, col = row, col + 1 local ns = require("go.ts.go").get_func_method_node_at_pos(row, col) if empty(ns) then return nil end if not string.find(ns.name, "[T|t]est") then -- not in a test function local fns = M.get_testfunc() for _, fn in ipairs(fns) do log(fn, ns.name) if string.find(fn:lower(), ns.name:lower()) then ns = { name = fn } return ns end end end return ns end M.test_func = function(...) local args = { ... } log(args) -- fpath = fpath:gsub(" ", [[\ ]]) -- fpath = fpath:gsub("-", [[\-]]) -- log(fpath) ns = M.get_test_func_name() if empty(ns) then return M.select_tests() end local optarg, optind, reminder = getopt.get_opts(args, short_opts, long_opts) local tags = get_build_tags(args) utils.log("parnode" .. vim.inspect(ns)) local test_runner = _GO_NVIM_CFG.go if _GO_NVIM_CFG.test_runner ~= "go" then require("go.install").install(test_runner) test_runner = _GO_NVIM_CFG.test_runner if test_runner == "ginkgo" then ginkgo.test_func(...) end end local run_flags = { "-run" } local cmd = {} local run_in_floaterm = optarg["F"] or _GO_NVIM_CFG.run_in_floaterm if run_in_floaterm then table.insert(cmd, test_runner) table.insert(cmd, "test") end if optarg["s"] then return M.select_tests() end if _GO_NVIM_CFG.verbose_tests and _GO_NVIM_CFG.test_runner == "go" then table.insert(cmd, "-v") end if not empty(tags) then cmd = vim.list_extend(cmd, tags) end if ns.name:find("Bench") then local bench = "-bench=" .. ns.name table.insert(cmd, bench) vim.list_extend(cmd, bench_opts) else vim.list_extend(cmd, run_flags) table.insert(cmd, [[^]] .. ns.name) end local fpath = "." .. sep .. vim.fn.fnamemodify(vim.fn.expand("%:h"), ":~:.") table.insert(cmd, fpath) if test_runner == "dlv" then cmd = { "dlv", "test", fpath, "--", "-test.run", "^" .. ns.name } local term = require("go.term").run term({ cmd = cmd, autoclose = false }) return end if run_in_floaterm then utils.log(cmd) local term = require("go.term").run term({ cmd = cmd, autoclose = false }) return end vim.list_extend(cmd, reminder) vim.cmd([[setl makeprg=]] .. test_runner .. [[\ test]]) -- set_efm() utils.log("test cmd", cmd) return require("go.asyncmake").make(unpack(cmd)) end M.test_file = function(...) local args = { ... } log(args) -- require sed -- local testcases = [[sed -n 's/func.*\(Test.*\)(.*/\1/p' | xargs | sed 's/ /\\\|/g']] -- local fpath = vim.fn.expand("%:p") local fpath = "." .. sep .. vim.fn.fnamemodify(vim.fn.expand("%:p"), ":~:.") -- utils.log(args) local cmd = [[cat ]] .. fpath .. [[| sed -n 's/func.*\(Test.*\)(.*/\1/p' | xargs | sed 's/ /\|/g']] -- TODO maybe with treesitter or lsp list all functions in current file and regex with Test if vim.fn.executable("sed") == 0 then M.test_package(...) return end local optarg, optind, reminder = getopt.get_opts(args, short_opts, long_opts) local run_in_floaterm = optarg["F"] or _GO_NVIM_CFG.run_in_floaterm local tests = vim.fn.systemlist(cmd) utils.log(cmd, tests) tests = tests[1] if vim.fn.empty(tests) == 1 then vim.notify("no test found fallback to package test", vim.lsp.log_levels.DEBUG) M.test_package(...) return end local tags, args2 = get_build_tags(args) local test_runner = _GO_NVIM_CFG.go if _GO_NVIM_CFG.test_runner ~= "go" then test_runner = _GO_NVIM_CFG.test_runner require("go.install").install(test_runner) if test_runner == "ginkgo" then ginkgo.test_func(...) end end local relpath = utils.rel_path(true) log(relpath) local cmd_args = {} if run_in_floaterm then table.insert(cmd_args, test_runner) table.insert(cmd_args, "test") end if _GO_NVIM_CFG.verbose_tests then table.insert(cmd_args, "-v") end if tags ~= nil and #tags > 1 then cmd_args = vim.list_extend(cmd_args, tags) end if args2 then cmd_args = vim.list_extend(cmd_args, args2) end table.insert(cmd_args, "-run") table.insert(cmd_args, tests) table.insert(cmd_args, relpath) if run_in_floaterm then local term = require("go.term").run term({ cmd = cmd_args, autoclose = false }) return end if _GO_NVIM_CFG.test_runner == "dlv" then cmd_args = { "dlv", "test", relpath, "--", "-test.run", tests } local term = require("go.term").run term({ cmd = cmd_args, autoclose = false }) return end vim.cmd([[setl makeprg=]] .. _GO_NVIM_CFG.go .. [[\ test]]) log(cmd_args) local cmdret = require("go.asyncmake").make(unpack(cmd_args)) utils.log("test cmd: ", cmd, " finished") return cmdret end -- TS based run func -- https://github.com/rentziass/dotfiles/blob/master/vim/.config/nvim/lua/rentziass/lsp/go_tests.lua M.run_file = function() local bufnr = vim.api.nvim_get_current_buf() local tree = vim.treesitter.get_parser(bufnr):parse()[1] local query = vim.treesitter.parse_query("go", require("go.ts.textobjects").query_test_func) local test_names = {} for id, node, metadata in query:iter_captures(tree:root(), bufnr, 0, -1) do local name = query.captures[id] -- name of the capture in the query if name == "test_name" then table.insert(test_names, vim.treesitter.query.get_node_text(node, bufnr)) end end vim.schedule(function() vim.lsp.buf.execute_command({ command = "gopls.run_tests", arguments = { { URI = vim.uri_from_bufnr(0), Tests = test_names } }, }) end) end M.get_testfunc = function() local bufnr = get_test_filebufnr() local parser = vim.treesitter.get_parser(bufnr) local tree = parser:parse() tree = tree[1] local query = vim.treesitter.parse_query("go", require("go.ts.go").query_test_func) local test_names = {} for id, node, metadata in query:iter_captures(tree:root(), bufnr, 0, -1) do local name = query.captures[id] -- name of the capture in the query if name == "test_name" then table.insert(test_names, vim.treesitter.query.get_node_text(node, bufnr)) end end return test_names end -- GUI to select test? M.select_tests = function() local guihua = utils.load_plugin("guihua.lua", "guihua.gui") local original_select = vim.ui.select if guihua then vim.ui.select = require("guihua.gui").select end vim.defer_fn(function() vim.ui.select = original_select end, 500) local function onselect(item, idx) if not item then return end local uri = vim.uri_from_bufnr(0) log(uri, item, idx) vim.schedule(function() vim.lsp.buf.execute_command({ command = "gopls.run_tests", arguments = { { URI = uri, Tests = { item } } }, }) end) end local test_names = M.get_testfunc() vim.ui.select(test_names, { prompt = "select test to run:", kind = "codelensaction", }, onselect) return test_names end return M