@@ -1,5 +1,11 @@
name: linters -on: [push, pull_request] + +on: + push: + branches: + - main + - develop + pull_request: jobs: linters:
@@ -1,25 +1,20 @@
name: tests -on: [push, pull_request] + +on: + push: + branches: + - main + - develop + pull_request: jobs: tests: strategy: matrix: os: [ubuntu-latest] - nvim_version: + version: + - v0.10.4 - nightly - - v0.7.0 - - v0.7.2 - - v0.8.0 - - v0.8.1 - - v0.8.2 - - v0.8.3 - - v0.9.0 - - v0.9.1 - - v0.9.2 - - v0.9.4 - - v0.9.5 - - v0.10.0 runs-on: ${{ matrix.os }} steps: - name: Install Task@@ -28,18 +23,33 @@ with:
version: 3.x repo-token: ${{ secrets.GITHUB_TOKEN }} + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: "1.24.0" + check-latest: false + + - name: Install NeoVim + uses: rhysd/action-setup-vim@v1 + with: + neovim: true + version: ${{ matrix.version }} + - uses: actions/checkout@v3 - - name: Install Neovim - run: | - mkdir -p /tmp/nvim - wget -q https://github.com/neovim/neovim/releases/download/${{ matrix.nvim_version }}/nvim.appimage -O /tmp/nvim/nvim.appimage - cd /tmp/nvim - chmod a+x ./nvim.appimage - ./nvim.appimage --appimage-extract - echo "/tmp/nvim/squashfs-root/usr/bin/" >> $GITHUB_PATH + - name: Cache .tests + uses: actions/cache@v4 + with: + path: | + ${{ github.workspace }}/.tests + ~/.cache/go-build + ~/go/pkg/mod + key: ${{ runner.os }}-tests-${{ hashFiles('${{ github.workspace }}/.tests') }} + + - name: Install Go bins + run: nvim --headless -u "./scripts/minimal_init.lua" -c "GoInstallDeps" -c "qa!" - name: Run Tests run: | nvim --version - task test + task tests
@@ -10,10 +10,11 @@ > If you want to use new and maybe undocumented, and unstable features you might use [develop](https://github.com/olexsmir/gopher.nvim/tree/develop) branch.
## Install (using [lazy.nvim](https://github.com/folke/lazy.nvim)) -Pre-dependency: +Requirements: -- [Go](https://github.com/golang/go) +- **Neovim 0.10** or later - `go` treesitter parser, install by `:TSInstall go` +- [Go](https://github.com/golang/go) installed (tested on 1.23) ```lua {
@@ -26,17 +26,13 @@ desc: runs lua formatter
cmds: - stylua . - test: - desc: runs all tests - aliases: [tests, spec] + tests: + desc: run all tests cmds: - | nvim --headless \ - -u ./scripts/minimal_init.lua \ - -c "PlenaryBustedDirectory spec \ - {minimal_init='./scripts/minimal_init.lua' \ - ,sequential=true}" \ - -c ":qa!" + -u ./scripts/minimal_init.lua \ + -c "lua MiniTest.run()" docgen: desc: generate vimhelp
@@ -116,7 +116,6 @@ Name string `yaml:name`
} < - ============================================================================== ------------------------------------------------------------------------------ *gopher.nvim-impl*@@ -147,7 +146,6 @@ panic("not implemented") // TODO: Implement
} < - ============================================================================== ------------------------------------------------------------------------------ *gopher.nvim-gotests*@@ -166,7 +164,6 @@ - run `:GoTestsExp`
you can also specify the template to use for generating the tests. see |gopher.nvim-config| more details about templates can be found at: https://github.com/cweill/gotests - ------------------------------------------------------------------------------ *gopher.nvim-gotests-named*
@@ -23,12 +23,4 @@ })
log.debug(msg) end --- safe require ----@param module string module name -function utils.sreq(module) - local ok, m = pcall(require, module) - assert(ok, string.format("gopher.nvim dependency error: %s not installed", module)) - return m -end - return utils
@@ -23,7 +23,7 @@
--minidoc_replace_start { ---@tag gopher.nvim-config-defaults ----@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section):gsub(">", ">lua") +---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section) --- ---@class gopher.Config local default_config = {
@@ -1,6 +1,9 @@
[vim] any = true +[MiniTest] +any = true + [describe] any = true [[describe.args]]
@@ -6,7 +6,7 @@
local function install_plug(plugin) local name = plugin:match ".*/(.*)" local package_root = root ".tests/site/pack/deps/start/" - if not vim.loop.fs_stat(package_root .. name) then + if not vim.uv.fs_stat(package_root .. name) then print("Installing " .. plugin) vim.fn.mkdir(package_root, "p") vim.fn.system {@@ -18,6 +18,11 @@ package_root .. "/" .. name,
} end end + +vim.env.XDG_CONFIG_HOME = root ".tests/config" +vim.env.XDG_DATA_HOME = root ".tests/data" +vim.env.XDG_STATE_HOME = root ".tests/state" +vim.env.XDG_CACHE_HOME = root ".tests/cache" vim.cmd [[set runtimepath=$VIMRUNTIME]] vim.opt.runtimepath:append(root())@@ -27,8 +32,18 @@
install_plug "nvim-lua/plenary.nvim" install_plug "nvim-treesitter/nvim-treesitter" install_plug "echasnovski/mini.doc" -- used for docs generation +install_plug "echasnovski/mini.test" -vim.env.XDG_CONFIG_HOME = root ".tests/config" -vim.env.XDG_DATA_HOME = root ".tests/data" -vim.env.XDG_STATE_HOME = root ".tests/state" -vim.env.XDG_CACHE_HOME = root ".tests/cache" +-- install go treesitter parse +require("nvim-treesitter.install").ensure_installed_sync "go" + +-- setup mini.test only when running headless nvim +if #vim.api.nvim_list_uis() == 0 then + require("mini.test").setup { + collect = { + find_files = function() + return vim.fn.globpath("spec", "**/*_test.lua", true, true) + end, + }, + } +end
@@ -0,0 +1,2 @@
+// Package main provides main +package main
@@ -0,0 +1,9 @@
+package main + +func test() error { + return nil +} + +func main() { + err := test() +}
@@ -0,0 +1,12 @@
+package main + +func test() error { + return nil +} + +func main() { + err := test() + if err != nil { + return + } +}
@@ -0,0 +1,8 @@
+package main + +type CloserTest2 struct{} + +func (closertest *CloserTest2) Close() error { + panic("not implemented") // TODO: Implement +} +
@@ -0,0 +1,8 @@
+package main + +func (r Read2) Read(p []byte) (n int, err error) { + panic("not implemented") // TODO: Implement +} + + +type Read2 struct{}
@@ -0,0 +1,8 @@
+package main + +type WriterTest2 struct{} + +func (w *WriterTest2) Write(p []byte) (n int, err error) { + panic("not implemented") // TODO: Implement +} +
@@ -0,0 +1,5 @@
+package fortest + +func Add(x, y int) int { + return 2 + x + y +}
@@ -0,0 +1,24 @@
+package fortest + +import "testing" + +func TestAdd(t *testing.T) { + type args struct { + x int + y int + } + tests := []struct { + name string + args args + want int + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := Add(tt.args.x, tt.args.y); got != tt.want { + t.Errorf("Add() = %v, want %v", got, tt.want) + } + }) + } +}
@@ -0,0 +1,7 @@
+package fortest + +type ForTest struct{} + +func (t *ForTest) Add(x, y int) int { + return 2 + x + y +}
@@ -0,0 +1,26 @@
+package fortest + +import "testing" + +func TestForTest_Add(t *testing.T) { + type args struct { + x int + y int + } + tests := []struct { + name string + tr *ForTest + args args + want int + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tr := &ForTest{} + if got := tr.Add(tt.args.x, tt.args.y); got != tt.want { + t.Errorf("ForTest.Add() = %v, want %v", got, tt.want) + } + }) + } +}
@@ -0,0 +1,27 @@
+local t = require "spec.testutils" + +local child = MiniTest.new_child_neovim() +local T = MiniTest.new_set { + hooks = { + post_once = child.stop, + pre_case = function() + MiniTest.skip "This module should be fixed first" + child.restart { "-u", t.mininit_path } + end, + }, +} +T["comment"] = MiniTest.new_set {} + +T["comment"]["should add comment to package"] = function() end + +T["comment"]["should add comment to struct"] = function() end + +T["comment"]["should add comment to function"] = function() end + +T["comment"]["should add comment to method"] = function() end + +T["comment"]["should add comment to interface"] = function() end + +T["comment"]["otherwise should add // above cursor"] = function() end + +return T
@@ -0,0 +1,47 @@
+local t = require "spec.testutils" + +local child = MiniTest.new_child_neovim() +local T = MiniTest.new_set { + hooks = { + post_once = child.stop, + pre_case = function() + child.restart { "-u", t.mininit_path } + end, + }, +} +T["gotests"] = MiniTest.new_set {} + +--- NOTE: :GoTestAdd is the only place that has actual logic +--- All other parts are handled `gotests` itself. + +---@param fpath string +---@return string +local function read_testfile(fpath) + return t.readfile(fpath:gsub(".go", "_test.go")) +end + +T["gotests"]["should add test for function under cursor"] = function() + local tmp = t.tmpfile() + local fixtures = t.get_fixtures "tests/function" + t.writefile(tmp, fixtures.input) + + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr "%", 3, 6 }) + child.cmd "GoTestAdd" + + t.eq(fixtures.output, read_testfile(tmp)) +end + +T["gotests"]["should add test for method under cursor"] = function() + local tmp = t.tmpfile() + local fixtures = t.get_fixtures "tests/method" + t.writefile(tmp, fixtures.input) + + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr "%", 5, 19 }) + child.cmd "GoTestAdd" + + t.eq(fixtures.output, read_testfile(tmp)) +end + +return T
@@ -0,0 +1,26 @@
+local t = require "spec.testutils" + +local child = MiniTest.new_child_neovim() +local T = MiniTest.new_set { + hooks = { + post_once = child.stop, + pre_case = function() + child.restart { "-u", t.mininit_path } + end, + }, +} +T["iferr"] = MiniTest.new_set {} +T["iferr"]["works"] = function() + local tmp = t.tmpfile() + local fixtures = t.get_fixtures "iferr/iferr" + t.writefile(tmp, fixtures.input) + + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr "%", 8, 2, 0 }) + child.cmd "GoIfErr" + child.cmd "write" + + t.eq(t.readfile(tmp), fixtures.output) +end + +return T
@@ -0,0 +1,55 @@
+local t = require "spec.testutils" + +local child = MiniTest.new_child_neovim() +local T = MiniTest.new_set { + hooks = { + post_once = child.stop, + pre_case = function() + child.restart { "-u", t.mininit_path } + end, + }, +} +T["impl"] = MiniTest.new_set {} +T["impl"]["works w io.Writer"] = function() + local tmp = t.tmpfile() + local fixtures = t.get_fixtures "impl/writer" + t.writefile(tmp, fixtures.input) + + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 6 }) + child.cmd "GoImpl w io.Writer" + child.cmd "write" + + -- NOTE: since "impl" won't implement interface if it's already implemented i went with this hack + local rhs = fixtures.output:gsub("Test2", "Test") + t.eq(t.readfile(tmp), rhs) +end + +T["impl"]["works r Read io.Reader"] = function() + local tmp = t.tmpfile() + local fixtures = t.get_fixtures "impl/reader" + t.writefile(tmp, fixtures.input) + + child.cmd("silent edit " .. tmp) + child.cmd "GoImpl r Read io.Reader" + child.cmd "write" + + local rhs = fixtures.output:gsub("Read2", "Read") + t.eq(t.readfile(tmp), rhs) +end + +T["impl"]["works io.Closer"] = function() + local tmp = t.tmpfile() + local fixtures = t.get_fixtures "impl/closer" + t.writefile(tmp, fixtures.input) + + child.cmd("silent edit " .. tmp) + child.fn.setpos(".", { child.fn.bufnr(tmp), 3, 6 }) + child.cmd "GoImpl io.Closer" + child.cmd "write" + + local rhs = fixtures.output:gsub("Test2", "Test") + t.eq(t.readfile(tmp), rhs) +end + +return T
@@ -0,0 +1,43 @@
+local base_dir = vim.env.GOPHER_DIR or vim.fn.expand "%:p:h" + +---@class gopher.TestUtils +local testutils = {} + +testutils.mininit_path = vim.fs.joinpath(base_dir, "scripts", "minimal_init.lua") +testutils.fixtures_dir = vim.fs.joinpath(base_dir, "spec/fixtures") + +---@generic T +---@param a T +---@param b T +---@return boolean +function testutils.eq(a, b) + return MiniTest.expect.equality(a, b) +end + +---@return string +function testutils.tmpfile() + return vim.fn.tempname() .. ".go" +end + +---@param path string +---@return string +function testutils.readfile(path) + return vim.fn.join(vim.fn.readfile(path), "\n") +end + +---@param fpath string +---@param contents string +function testutils.writefile(fpath, contents) + vim.fn.writefile(vim.split(contents, "\n"), fpath) +end + +---@param fixture string +---@return {input: string, output: string} +function testutils.get_fixtures(fixture) + return { + input = testutils.readfile(vim.fs.joinpath(testutils.fixtures_dir, fixture) .. "_input.go"), + output = testutils.readfile(vim.fs.joinpath(testutils.fixtures_dir, fixture) .. "_output.go"), + } +end + +return testutils
@@ -1,29 +0,0 @@
-describe("gopher.config", function() - it(".setup() should provide default when .setup() is not called", function() - local c = require "gopher.config" - - assert.are.same(c.commands.go, "go") - assert.are.same(c.commands.gomodifytags, "gomodifytags") - assert.are.same(c.commands.gotests, "gotests") - assert.are.same(c.commands.impl, "impl") - assert.are.same(c.commands.iferr, "iferr") - assert.are.same(c.commands.dlv, "dlv") - end) - - it(".setup() should change options on users config", function() - local c = require "gopher.config" - c.setup { - commands = { - go = "go1.420", - gomodifytags = "iDontUseRustBtw", - }, - } - - assert.are.same(c.commands.go, "go1.420") - assert.are.same(c.commands.gomodifytags, "iDontUseRustBtw") - assert.are.same(c.commands.gotests, "gotests") - assert.are.same(c.commands.impl, "impl") - assert.are.same(c.commands.iferr, "iferr") - assert.are.same(c.commands.dlv, "dlv") - end) -end)
@@ -1,15 +0,0 @@
-describe("gopher._utils", function() - local u = require "gopher._utils" - - describe(".sreq()", function() - it("can require existing module", function() - assert.are.same(require "gopher", u.sreq "gopher") - end) - - it("cannot require non-existing module", function() - assert.has.errors(function() - u.sreq "iDontExistBtw" - end) - end) - end) -end)