From 72c7c9b828a4bf62f8745ce01d2bc4a84e1ce44c Mon Sep 17 00:00:00 2001 From: fidgetingbits Date: Mon, 17 Jun 2024 14:24:50 +0800 Subject: [PATCH 01/14] initial lua test skeleton --- .gitignore | 1 + cursorless.nvim/.busted | 8 ++ cursorless.nvim/lua/cursorless/cursorless.lua | 11 +-- cursorless.nvim/test/nvim-shim.sh | 45 +++++++++ cursorless.nvim/test/unit/cursorless_spec.lua | 95 +++++++++++++++++++ docs/contributing/cursorless-in-neovim.md | 10 +- 6 files changed, 160 insertions(+), 10 deletions(-) create mode 100644 cursorless.nvim/.busted create mode 100755 cursorless.nvim/test/nvim-shim.sh create mode 100644 cursorless.nvim/test/unit/cursorless_spec.lua diff --git a/.gitignore b/.gitignore index 0013e6e813..d178447075 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ packages/test-harness/testSubsetGrep.properties # cursorless-neovim cursorless.nvim/node/cursorless-neovim cursorless.nvim/node/test-harness +cursorless.nvim/test/xdg/ # nix .direnv/ diff --git a/cursorless.nvim/.busted b/cursorless.nvim/.busted new file mode 100644 index 0000000000..6aaf242404 --- /dev/null +++ b/cursorless.nvim/.busted @@ -0,0 +1,8 @@ +return { + _all = { + lua = './test/nvim-shim.sh' + }, + unit = { + ROOT = {'./test/unit/'}, + }, +} diff --git a/cursorless.nvim/lua/cursorless/cursorless.lua b/cursorless.nvim/lua/cursorless/cursorless.lua index 69e641f7df..36349c900c 100644 --- a/cursorless.nvim/lua/cursorless/cursorless.lua +++ b/cursorless.nvim/lua/cursorless/cursorless.lua @@ -49,14 +49,9 @@ function M.buffer_get_selection() end -- https://github.com/nvim-treesitter/nvim-treesitter/blob/master/lua/nvim-treesitter/ts_utils.lua#L278 --- luacheck:ignore 631 --- https://github.com/nvim-treesitter/nvim-treesitter-textobjects/blob/master/lua/nvim-treesitter/textobjects/select.lua#L114 --- as an example if you put that in a vim buffer and do the following you can do a selection: --- :w c:\work\tmp\test.lua --- :so % --- :lua select_range(5, 12, 5, 30) --- for example it will highlight the last function name (nvim_win_set_cursor). --- another example is :tmap lua require("talon.cursorless").select_range(4, 0, 4, 38) +-- If you have a buffer with the line: "hello world" +-- :lua require("cursorless.cursorless").select_range(1, 2, 1, 4) +-- will highlight "llo" -- NOTE: works for any mode (n,i,v,nt) except in t mode function M.select_range(start_line, start_col, end_line, end_col) vim.cmd([[normal! :noh]]) diff --git a/cursorless.nvim/test/nvim-shim.sh b/cursorless.nvim/test/nvim-shim.sh new file mode 100755 index 0000000000..6ca53a9f20 --- /dev/null +++ b/cursorless.nvim/test/nvim-shim.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +set -euo pipefail + +export XDG_CONFIG_HOME='test/xdg/config/' +export XDG_STATE_HOME='test/xdg/local/state/' +export XDG_DATA_HOME='test/xdg/local/share/' + +# FIXME: For the flake.nix devshell I plan to eventually just install the unwrapped binary straight up so I can +# reference it for these tests, but until then... +function resolve_nvim_path() { + nvim=$(type -p "nvim" | awk '{print $NF}') + if file "${nvim}" | grep -q 'executable'; then + echo "${nvim}" + return + fi + + # On NixOS, this might still be a wrapper, so we need to make sure we find the real binary + current_link="${nvim}" + while [ -L "${current_link}" ]; do + current_link=$(readlink "${current_link}") + done + nvim="${current_link}" + + if file "${current_link}" | grep -q 'script'; then + nvim=$(grep 'exec -a' "${current_link}" | cut -f4 -d" " | tr -d '"') + fi + echo "${nvim}" +} + +mkdir -p ${XDG_CONFIG_HOME} ${XDG_STATE_HOME} ${XDG_DATA_HOME} +mkdir -p ${XDG_DATA_HOME}/nvim/site/pack/testing/start || true +link_path="${XDG_DATA_HOME}/nvim/site/pack/testing/start/cursorless.nvim" +if [ ! -e "${link_path}" ]; then + # FIXME: Expects to be in cursorless.nvim, so we should force cd here? + ln -sf "${PWD}" ${link_path} +fi + +NVIM=$(resolve_nvim_path) + +# shellcheck disable=SC2068 +${NVIM} --cmd 'set loadplugins' -l $@ +exit_code=$? +rm ${link_path} 2>/dev/null || true + +exit $exit_code diff --git a/cursorless.nvim/test/unit/cursorless_spec.lua b/cursorless.nvim/test/unit/cursorless_spec.lua new file mode 100644 index 0000000000..f9770b1157 --- /dev/null +++ b/cursorless.nvim/test/unit/cursorless_spec.lua @@ -0,0 +1,95 @@ +describe("cursorless tests", function() + local cursorless = require("cursorless.cursorless") + + local get_selected_text = function() + local _, ls, cs = unpack(vim.fn.getpos("v")) + local _, le, ce = unpack(vim.fn.getpos(".")) + return vim.api.nvim_buf_get_text(0, ls - 1, cs - 1, le - 1, ce, {}) + end + + local function convert_table_entries(tbl, func) + local mapped = {} + for k, v in pairs(tbl) do + mapped[k] = func(v) + end + return mapped + end + + describe("window_get_visible_lines()", function() + before_each(function() end) + + it("Can read one visible line", function() + local pos = vim.api.nvim_win_get_cursor(0)[2] + local line = vim.api.nvim_get_current_line() + local nline = line:sub(0, pos) .. "hello" .. line:sub(pos + 1) + vim.api.nvim_set_current_line(nline) + + local visible = cursorless.window_get_visible_lines() + assert(table.concat(visible) == table.concat({ 1, 1 })) + end) + + it("Can read all lines visible on the window", function() + local maxlines = vim.api.nvim_win_get_height(0) + local lines = {} + for _ = 1, (maxlines + 1) do + table.insert(lines, "hello ") + end + vim.api.nvim_buf_set_lines(0, 0, -1, false, lines) + local visible = cursorless.window_get_visible_lines() + assert(table.concat(visible) == table.concat({ 1, maxlines })) + end) + end) + describe("select_range()", function() + it("Selects the specified range", function() + local lines = "hello world" + vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(lines, "\n")) + cursorless.select_range(1, 2, 1, 4) + + assert(table.concat(get_selected_text()) == "llo") + end) + end) + describe("buffer_get_selection()", function() + it( + "Can get the forward selection in a format expected by cursorless", + function() + local lines = "hello world" + vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(lines, "\n")) + cursorless.select_range(1, 2, 1, 4) + assert( + table.concat( + convert_table_entries(cursorless.buffer_get_selection(), tostring), + ", " + ) + == table.concat( + convert_table_entries({ 1, 3, 1, 5, false }, tostring), + ", " + ) + ) + end + ) + it( + "Can get the backward selection in a format expected by cursorless", + function() + local lines = "hello world" + vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(lines, "\n")) + cursorless.select_range(1, 4, 1, 2) + print( + table.concat( + convert_table_entries(cursorless.buffer_get_selection(), tostring), + ", " + ) + ) + assert( + table.concat( + convert_table_entries(cursorless.buffer_get_selection(), tostring), + ", " + ) + == table.concat( + convert_table_entries({ 1, 3, 1, 5, true }, tostring), + ", " + ) + ) + end + ) + end) +end) diff --git a/docs/contributing/cursorless-in-neovim.md b/docs/contributing/cursorless-in-neovim.md index 9d50c73612..fa09491a8f 100644 --- a/docs/contributing/cursorless-in-neovim.md +++ b/docs/contributing/cursorless-in-neovim.md @@ -12,7 +12,7 @@ Follow the steps in [CONTRIBUTING.md](./CONTRIBUTING.md#initial-setup). Follow the installation steps in [cursorless.nvim](https://github.com/hands-free-vim/cursorless.nvim/tree/main#prerequisites). -Confirm that production cursorless.nvim is working in neovim, eg say `"take first paint"` in a nonempty document. +Confirm that production cursorless.nvim is working in neovim, eg say `"take first paint"` in a non-empty document. ### 3. Add nvim executable path to your PATH @@ -40,7 +40,13 @@ debug mode. To do so you need to run the `workbench.action.debug.selectandstart` The debug logs are written in `C:\path\to\cursorless\packages\cursorless-neovim\out\nvim_node.log`. -NOTE: This will spawn a standalone nvim instance that is independent of VSCode. Consequently after you're done debugging, you need to close nvim. +NOTE: This will spawn a standalone nvim instance that is independent of VSCode. Consequently after you're done +debugging, you need to close nvim. + +### Running lua tests + +Their are separate cursorless and lua tests. You can run the lua tests by entering the `cursorless.nvim` folder and +running: `busted --run unit`. These tests currently only work on Linux. ## Sending pull requests From dcaab6f7284f1a2fc0b8476f16bb60f924118cbb Mon Sep 17 00:00:00 2001 From: fidgetingbits Date: Wed, 19 Jun 2024 10:48:14 +0800 Subject: [PATCH 02/14] feat: test adding busted workflow --- .github/workflows/test.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fd6ba2b819..36231d6886 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -55,6 +55,11 @@ jobs: if: runner.os == 'Linux' && matrix.app_version == 'stable' env: NEOVIM_PATH: ${{ steps.vim.outputs.executable }} + - name: Run neovim lua unit tests using busted + id: neovimLuaTests + uses: lunarmodules/busted@v2.2.0 + - run: xvfb-run -a cd cursorless.nvim && busted --run unit + if: runner.os == 'Linux' && matrix.app_version == 'stable' - name: Create vscode dist that can be installed locally run: pnpm -F @cursorless/cursorless-vscode populate-dist --local-install if: runner.os == 'Linux' && matrix.app_version == 'stable' From 682c520cd3179e8c130afec2108748d32af6610a Mon Sep 17 00:00:00 2001 From: Cedric Halbronn Date: Fri, 19 Jul 2024 14:53:53 +0100 Subject: [PATCH 03/14] fix: only install busted on Linux --- .github/workflows/test.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 36231d6886..c8f8bb19b4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -55,10 +55,11 @@ jobs: if: runner.os == 'Linux' && matrix.app_version == 'stable' env: NEOVIM_PATH: ${{ steps.vim.outputs.executable }} - - name: Run neovim lua unit tests using busted + - uses: lunarmodules/busted@v2.2.0 + if: runner.os == 'Linux' && matrix.app_version == 'stable' id: neovimLuaTests - uses: lunarmodules/busted@v2.2.0 - - run: xvfb-run -a cd cursorless.nvim && busted --run unit + - name: Run neovim lua unit tests + run: xvfb-run -a cd cursorless.nvim && busted --run unit if: runner.os == 'Linux' && matrix.app_version == 'stable' - name: Create vscode dist that can be installed locally run: pnpm -F @cursorless/cursorless-vscode populate-dist --local-install From d113ab8ab1ffeb8c7965ac413c67d07995615c57 Mon Sep 17 00:00:00 2001 From: fidgetingbits Date: Sun, 21 Jul 2024 12:53:55 +0800 Subject: [PATCH 04/14] refactor: Address review feedback --- .gitignore | 1 - cursorless.nvim/test/helpers.lua | 16 ++++++ cursorless.nvim/test/nvim-shim.sh | 51 +++++++------------ cursorless.nvim/test/unit/cursorless_spec.lua | 39 ++++++-------- flake.nix | 3 ++ init.lua | 2 +- 6 files changed, 53 insertions(+), 59 deletions(-) create mode 100644 cursorless.nvim/test/helpers.lua diff --git a/.gitignore b/.gitignore index d178447075..0013e6e813 100644 --- a/.gitignore +++ b/.gitignore @@ -47,7 +47,6 @@ packages/test-harness/testSubsetGrep.properties # cursorless-neovim cursorless.nvim/node/cursorless-neovim cursorless.nvim/node/test-harness -cursorless.nvim/test/xdg/ # nix .direnv/ diff --git a/cursorless.nvim/test/helpers.lua b/cursorless.nvim/test/helpers.lua new file mode 100644 index 0000000000..500191d14b --- /dev/null +++ b/cursorless.nvim/test/helpers.lua @@ -0,0 +1,16 @@ +-- This file gets linked into plugin/helpers.lua of busted nvim config +-- Functions that are exposed to all tests + +function _G.get_selected_text() + local _, ls, cs = unpack(vim.fn.getpos("v")) + local _, le, ce = unpack(vim.fn.getpos(".")) + return vim.api.nvim_buf_get_text(0, ls - 1, cs - 1, le - 1, ce, {}) +end + +function _G.convert_table_entries(tbl, func) + local mapped = {} + for k, v in pairs(tbl) do + mapped[k] = func(v) + end + return mapped +end diff --git a/cursorless.nvim/test/nvim-shim.sh b/cursorless.nvim/test/nvim-shim.sh index 6ca53a9f20..860a6e9b2b 100755 --- a/cursorless.nvim/test/nvim-shim.sh +++ b/cursorless.nvim/test/nvim-shim.sh @@ -1,45 +1,28 @@ #!/usr/bin/env bash set -euo pipefail -export XDG_CONFIG_HOME='test/xdg/config/' -export XDG_STATE_HOME='test/xdg/local/state/' -export XDG_DATA_HOME='test/xdg/local/share/' - -# FIXME: For the flake.nix devshell I plan to eventually just install the unwrapped binary straight up so I can -# reference it for these tests, but until then... -function resolve_nvim_path() { - nvim=$(type -p "nvim" | awk '{print $NF}') - if file "${nvim}" | grep -q 'executable'; then - echo "${nvim}" - return - fi +if ! [[ "${PWD}" == *"cursorless.nvim" ]]; then + echo "ERROR: This script must be run from inside cursorless.nvim/ directory" + exit 1 +fi - # On NixOS, this might still be a wrapper, so we need to make sure we find the real binary - current_link="${nvim}" - while [ -L "${current_link}" ]; do - current_link=$(readlink "${current_link}") - done - nvim="${current_link}" +test_folder=$(mktemp -d "${TMPDIR-/tmp}"/cursorless-busted-test-XXXXX) +export XDG_CONFIG_HOME="${test_folder}/xdg/config/" +export XDG_STATE_HOME="${test_folder}/xdg/local/state/" +export XDG_DATA_HOME="${test_folder}/xdg/local/share/" +dependency_folder="${XDG_DATA_HOME}/nvim/site/pack/testing/start/" +plugin_folder="${XDG_CONFIG_HOME}/nvim/plugin/" - if file "${current_link}" | grep -q 'script'; then - nvim=$(grep 'exec -a' "${current_link}" | cut -f4 -d" " | tr -d '"') - fi - echo "${nvim}" -} +mkdir -p "${plugin_folder}" "${XDG_STATE_HOME}" "${dependency_folder}" +ln -sf "${PWD}" "${dependency_folder}/cursorless.nvim" -mkdir -p ${XDG_CONFIG_HOME} ${XDG_STATE_HOME} ${XDG_DATA_HOME} -mkdir -p ${XDG_DATA_HOME}/nvim/site/pack/testing/start || true -link_path="${XDG_DATA_HOME}/nvim/site/pack/testing/start/cursorless.nvim" -if [ ! -e "${link_path}" ]; then - # FIXME: Expects to be in cursorless.nvim, so we should force cd here? - ln -sf "${PWD}" ${link_path} -fi - -NVIM=$(resolve_nvim_path) +# Link in standalone helper functions we want all tests to be able to call +ln -sf "${PWD}/test/helpers.lua" "${plugin_folder}/helpers.lua" # shellcheck disable=SC2068 -${NVIM} --cmd 'set loadplugins' -l $@ +command nvim --cmd 'set loadplugins' -l $@ exit_code=$? -rm ${link_path} 2>/dev/null || true + +rm -rf "${test_folder}" exit $exit_code diff --git a/cursorless.nvim/test/unit/cursorless_spec.lua b/cursorless.nvim/test/unit/cursorless_spec.lua index f9770b1157..c20e85b456 100644 --- a/cursorless.nvim/test/unit/cursorless_spec.lua +++ b/cursorless.nvim/test/unit/cursorless_spec.lua @@ -1,23 +1,7 @@ -describe("cursorless tests", function() +describe("cursorless.nvim tests", function() local cursorless = require("cursorless.cursorless") - local get_selected_text = function() - local _, ls, cs = unpack(vim.fn.getpos("v")) - local _, le, ce = unpack(vim.fn.getpos(".")) - return vim.api.nvim_buf_get_text(0, ls - 1, cs - 1, le - 1, ce, {}) - end - - local function convert_table_entries(tbl, func) - local mapped = {} - for k, v in pairs(tbl) do - mapped[k] = func(v) - end - return mapped - end - describe("window_get_visible_lines()", function() - before_each(function() end) - it("Can read one visible line", function() local pos = vim.api.nvim_win_get_cursor(0)[2] local line = vim.api.nvim_get_current_line() @@ -45,7 +29,7 @@ describe("cursorless tests", function() vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(lines, "\n")) cursorless.select_range(1, 2, 1, 4) - assert(table.concat(get_selected_text()) == "llo") + assert(table.concat(_G.get_selected_text()) == "llo") end) end) describe("buffer_get_selection()", function() @@ -57,11 +41,14 @@ describe("cursorless tests", function() cursorless.select_range(1, 2, 1, 4) assert( table.concat( - convert_table_entries(cursorless.buffer_get_selection(), tostring), + _G.convert_table_entries( + cursorless.buffer_get_selection(), + tostring + ), ", " ) == table.concat( - convert_table_entries({ 1, 3, 1, 5, false }, tostring), + _G.convert_table_entries({ 1, 3, 1, 5, false }, tostring), ", " ) ) @@ -75,17 +62,23 @@ describe("cursorless tests", function() cursorless.select_range(1, 4, 1, 2) print( table.concat( - convert_table_entries(cursorless.buffer_get_selection(), tostring), + _G.convert_table_entries( + cursorless.buffer_get_selection(), + tostring + ), ", " ) ) assert( table.concat( - convert_table_entries(cursorless.buffer_get_selection(), tostring), + _G.convert_table_entries( + cursorless.buffer_get_selection(), + tostring + ), ", " ) == table.concat( - convert_table_entries({ 1, 3, 1, 5, true }, tostring), + _G.convert_table_entries({ 1, 3, 1, 5, true }, tostring), ", " ) ) diff --git a/flake.nix b/flake.nix index 2eaae2e54a..a3b2009254 100644 --- a/flake.nix +++ b/flake.nix @@ -76,7 +76,10 @@ ''; })) python + pkgs.neovim + pkgs.luajitPackages.busted # for lua testing + pkgs.luarocks # pre-commit doesn't auto-install luarocks ]; # To prevent weird broken non-interactive bash terminal buildInputs = [ pkgs.bashInteractive ]; diff --git a/init.lua b/init.lua index da81c08b16..8ef08d16cb 100644 --- a/init.lua +++ b/init.lua @@ -10,7 +10,7 @@ if not (vim.uv or vim.loop).fs_stat(lazypath) then lazypath, }) end -vim.opt.rtp:prepend(lazypath) +vim.opt.runtimepath:prepend(lazypath) require("lazy").setup({ -- Allows title detection by neovim-talon while testing "hands-free-vim/talon.nvim", From e54d40e5b991c4a8449d1d847e9daf10b891cd75 Mon Sep 17 00:00:00 2001 From: fidgetingbits Date: Mon, 22 Jul 2024 09:33:05 +0800 Subject: [PATCH 05/14] fix: change workflows to actually work --- .github/workflows/test.yml | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c8f8bb19b4..69210e28eb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -55,12 +55,27 @@ jobs: if: runner.os == 'Linux' && matrix.app_version == 'stable' env: NEOVIM_PATH: ${{ steps.vim.outputs.executable }} - - uses: lunarmodules/busted@v2.2.0 + - name: Set up Lua + uses: leafo/gh-actions-lua@v9 + with: + luaVersion: "luajit-2.1.0-beta3" if: runner.os == 'Linux' && matrix.app_version == 'stable' - id: neovimLuaTests - - name: Run neovim lua unit tests - run: xvfb-run -a cd cursorless.nvim && busted --run unit + - name: Set up Luarocks + uses: leafo/gh-actions-luarocks@v4 + with: + luaVersion: "5.1" if: runner.os == 'Linux' && matrix.app_version == 'stable' + - name: Install Busted + run: | + luarocks install busted + luarocks install luafilesystem + if: runner.os == 'Linux' && matrix.app_version == 'stable' + - name: Run cursorless.nvim busted lua tests + run: | + cd cursorless.nvim + busted --run unit + if: runner.os == 'Linux' && matrix.app_version == 'stable' + id: neovimLuaTests - name: Create vscode dist that can be installed locally run: pnpm -F @cursorless/cursorless-vscode populate-dist --local-install if: runner.os == 'Linux' && matrix.app_version == 'stable' From b7a80c1537ae1c3fa68e746f4c45850cd811cdb9 Mon Sep 17 00:00:00 2001 From: fidgetingbits Date: Mon, 22 Jul 2024 21:23:49 +0800 Subject: [PATCH 06/14] fix: Address review feedback --- .github/workflows/test.yml | 16 ++++------------ .vscode/tasks.json | 8 ++++++++ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 69210e28eb..ac745cbd41 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -55,27 +55,19 @@ jobs: if: runner.os == 'Linux' && matrix.app_version == 'stable' env: NEOVIM_PATH: ${{ steps.vim.outputs.executable }} - - name: Set up Lua - uses: leafo/gh-actions-lua@v9 + - uses: leafo/gh-actions-lua@v9 with: luaVersion: "luajit-2.1.0-beta3" if: runner.os == 'Linux' && matrix.app_version == 'stable' - - name: Set up Luarocks - uses: leafo/gh-actions-luarocks@v4 + - uses: leafo/gh-actions-luarocks@v4 with: luaVersion: "5.1" if: runner.os == 'Linux' && matrix.app_version == 'stable' - - name: Install Busted - run: | - luarocks install busted - luarocks install luafilesystem + - run: luarocks install busted luafilesystem if: runner.os == 'Linux' && matrix.app_version == 'stable' - name: Run cursorless.nvim busted lua tests - run: | - cd cursorless.nvim - busted --run unit + run: cd cursorless.nvim && busted --run unit if: runner.os == 'Linux' && matrix.app_version == 'stable' - id: neovimLuaTests - name: Create vscode dist that can be installed locally run: pnpm -F @cursorless/cursorless-vscode populate-dist --local-install if: runner.os == 'Linux' && matrix.app_version == 'stable' diff --git a/.vscode/tasks.json b/.vscode/tasks.json index c8b9263443..13dedcd080 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -268,6 +268,14 @@ "command": "echo" } }, + { + "label": "Neovim: Run cursorless.nvim lua tests", + "type": "shell", + "command": "busted --run unit", + "options": { + "cwd": "cursorless.nvim" + } + }, // cursorless.org { From 1bff7272cf7696ac919cfb742616118955b2fd6b Mon Sep 17 00:00:00 2001 From: fidgetingbits Date: Mon, 22 Jul 2024 22:59:50 +0800 Subject: [PATCH 07/14] fix: Address task.json feedback --- .vscode/tasks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 32e939a5f3..d17eb70368 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -288,7 +288,7 @@ // so we need to show logs outside of vscode (see #2454) }, { - "label": "Neovim: Run cursorless.nvim lua tests", + "label": "Neovim: Test Lua", "type": "shell", "command": "busted --run unit", "options": { From 46895579bdce231fc4c020addbccabda699688a6 Mon Sep 17 00:00:00 2001 From: fidgetingbits Date: Tue, 23 Jul 2024 08:29:29 +0800 Subject: [PATCH 08/14] docs: Add neovim lua test architecture section --- docs/contributing/architecture/tests.md | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/docs/contributing/architecture/tests.md b/docs/contributing/architecture/tests.md index 57a68ab45a..1f1254aa7f 100644 --- a/docs/contributing/architecture/tests.md +++ b/docs/contributing/architecture/tests.md @@ -31,7 +31,8 @@ packages/test-harness/src/config/init.lua -> TestHarnessRun() -> run() -> runAllTests() -> Mocha + packages/cursorless-neovim-e2e/src/suite/recorded.neovim.test.ts ``` -4. **Lua unit tests for neovim**: tests lua functions. They need to be executed in neovim. There is no such test for VSCode because we assume the functions provided by [VSCode APIs](https://code.visualstudio.com/api/references/vscode-api) have already been tested by Microsoft and work properly +4. **Lua unit tests for neovim**: tests lua functions implemented in cursorless.nvim. These tests are + executed inside neovim. 5. **Cursorless tests for Talon**: XXX @@ -293,7 +294,26 @@ NOTE: CI uses `dist/cursorless.nvim/` (and not `cursorless.nvim/`), since the sy ## 4. Lua unit tests for neovim -XXX +Many of the cursorless.nvim lua functions are run in order to complete cursorless actions and so are already +indirectly tested by the tests described in the [prevous section](#3.-cursorless-tests-for-neovim). Nevertheless, we run +more specific unit tests in order to give better visibility into exactly which functions are failing. + +The [busted](https://github.com/lunarmodules/busted) framework is used to test lua functions defined in cursorless.nvim. +This relies on a `./cursorless.nvim/.busted` file which directs busted to use a shell wrapper +`.cursorless.nvim/test/nvim-shim.sh` as its lua interpreter. It also declares that test specifications files are in +`./cursorless.nvim/test/unit/`. Any file in that folder ending with `_spec.lua` contains tests and will be executed +by neovim's lua interpreter, which allows neovim's internal API to be exposed to the tests. + +The `nvim-shim.sh` script sets up an enclosed neovim environment by using [XDG Base +Directory](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) environment variables (Linux +only) pointing to a temp directory. This allows loading any helpers and (optional) plugins needed to run +the tests. + +Afterwards, the shim will used `nvim` to execute whatever test spec file was passed by busted. Different tests rely on +the same custom helper functions. These functions are exposed as globals in a file called `helpers.lua` placed in `nvim/plugin/` insnide the isolated XDG environment. These helpers themselves also have their own unit tests that will be run by busted. + +This busted setup was inspired by this [blog +post](https://hiphish.github.io/blog/2024/01/29/testing-neovim-plugins-with-busted/), which goes into greater detail. ## 5. Cursorless tests for Talon From d1be1eaa5d8cca1ad7bda838c2cb3306b70c04cd Mon Sep 17 00:00:00 2001 From: fidgetingbits Date: Tue, 23 Jul 2024 09:28:13 +0800 Subject: [PATCH 09/14] refactor: switch to standalone neovim lua test action --- .github/actions/test-neovim-lua/action.yml | 19 +++++++++++++++++++ .github/workflows/test.yml | 13 +------------ 2 files changed, 20 insertions(+), 12 deletions(-) create mode 100644 .github/actions/test-neovim-lua/action.yml diff --git a/.github/actions/test-neovim-lua/action.yml b/.github/actions/test-neovim-lua/action.yml new file mode 100644 index 0000000000..91d6c97f01 --- /dev/null +++ b/.github/actions/test-neovim-lua/action.yml @@ -0,0 +1,19 @@ +name: "Neovim Lua Tests" +description: "Set up Neovim Lua environment and run Busted tests" +runs: + using: "composite" + steps: + - uses: leafo/gh-actions-lua@v9 + with: + luaVersion: "luajit-2.1.0-beta3" + - uses: leafo/gh-actions-luarocks@v4 + with: + luaVersion: "5.1" + - shell: bash + run: | + luarocks install busted + luarocks install luafilesystem + - shell: bash + run: | + cd cursorless.nvim + busted --run unit diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f2250c5733..30cc037002 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -56,18 +56,7 @@ jobs: if: runner.os == 'Linux' && matrix.app_version == 'stable' env: NEOVIM_PATH: ${{ steps.vim.outputs.executable }} - - uses: leafo/gh-actions-lua@v9 - with: - luaVersion: "luajit-2.1.0-beta3" - if: runner.os == 'Linux' && matrix.app_version == 'stable' - - uses: leafo/gh-actions-luarocks@v4 - with: - luaVersion: "5.1" - if: runner.os == 'Linux' && matrix.app_version == 'stable' - - run: luarocks install busted luafilesystem - if: runner.os == 'Linux' && matrix.app_version == 'stable' - - name: Run cursorless.nvim busted lua tests - run: cd cursorless.nvim && busted --run unit + - uses: ./.github/actions/test-neovim-lua/ if: runner.os == 'Linux' && matrix.app_version == 'stable' - name: Create vscode dist that can be installed locally run: pnpm -F @cursorless/cursorless-vscode populate-dist --local-install From 13514bc6fca12135e34e72e5955826e406195c43 Mon Sep 17 00:00:00 2001 From: fidgetingbits Date: Tue, 23 Jul 2024 09:39:07 +0800 Subject: [PATCH 10/14] fix: Remove unneeded luaVersion entry for luarocks --- .github/actions/test-neovim-lua/action.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/actions/test-neovim-lua/action.yml b/.github/actions/test-neovim-lua/action.yml index 91d6c97f01..d769fbaac2 100644 --- a/.github/actions/test-neovim-lua/action.yml +++ b/.github/actions/test-neovim-lua/action.yml @@ -7,8 +7,6 @@ runs: with: luaVersion: "luajit-2.1.0-beta3" - uses: leafo/gh-actions-luarocks@v4 - with: - luaVersion: "5.1" - shell: bash run: | luarocks install busted From 3bef6603186b8e1faea7b8935f06cfb0cc351530 Mon Sep 17 00:00:00 2001 From: Cedric Halbronn Date: Tue, 23 Jul 2024 11:50:29 +0100 Subject: [PATCH 11/14] docs: review busted tests documentation --- docs/contributing/architecture/tests.md | 60 +++++++++++++++++++------ 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/docs/contributing/architecture/tests.md b/docs/contributing/architecture/tests.md index 1f1254aa7f..e384b15d11 100644 --- a/docs/contributing/architecture/tests.md +++ b/docs/contributing/architecture/tests.md @@ -2,11 +2,11 @@ Tests in Cursorless are done in various ways depending on what is tested. -1. **Cursorless tests for VSCode**: tests the actions (`take`, `chuck`, etc.) inside VSCode +1. **Cursorless tests for VSCode**: tests the actions (`take`, `chuck`, etc.) inside VSCode. -2. **Cursorless unit tests**: tests specific TypeScript functions (`toggle decorations`, `visible multiple regions`, etc.). These tests don't rely on VSCode or neovim +2. **Cursorless unit tests**: tests specific TypeScript functions (`toggle decorations`, `visible multiple regions`, etc.). These tests don't rely on VSCode or neovim. -3. **Cursorless tests for neovim**: tests the actions (`take`, `chuck`, etc.) inside neovim. 99% of them are the same as the "Cursorless tests for VSCode" except some new ones are needed in order to test all actions, due to no hats being supported yet in neovim (and the fact that lots of "Cursorless tests for VSCode" rely on hats) +3. **Cursorless tests for neovim**: tests the actions (`take`, `chuck`, etc.) inside neovim. 99% of them are the same as the "Cursorless tests for VSCode" except some new ones are needed in order to test all actions, due to no hats being supported yet in neovim (and the fact that lots of "Cursorless tests for VSCode" rely on hats). ``` // 3.1 Cursorless tests for neovim locally @@ -34,6 +34,24 @@ packages/test-harness/src/config/init.lua 4. **Lua unit tests for neovim**: tests lua functions implemented in cursorless.nvim. These tests are executed inside neovim. +``` +// 4.1 Lua unit tests for neovim locally +launch.json -> .vscode/tasks.json -> cd cursorless.nvim && busted --run unit + +cursorless.nvim/.busted + -> lua interpreter: cursorless.nvim/test/nvim-shim.sh -> nvim -l + -> test specification files: cursorless.nvim/test/unit/*_spec.lua +``` + +``` +// 4.2 Lua unit tests for neovim on CI +.github/workflows/test.yml -> .github/actions/test-neovim-lua/action.yml -> cd cursorless.nvim && busted --run unit + +cursorless.nvim/.busted + -> lua interpreter: cursorless.nvim/test/nvim-shim.sh -> nvim -l + -> test specification files: cursorless.nvim/test/unit/*_spec.lua +``` + 5. **Cursorless tests for Talon**: XXX ## 1. Cursorless tests for VSCode @@ -294,23 +312,37 @@ NOTE: CI uses `dist/cursorless.nvim/` (and not `cursorless.nvim/`), since the sy ## 4. Lua unit tests for neovim -Many of the cursorless.nvim lua functions are run in order to complete cursorless actions and so are already -indirectly tested by the tests described in the [prevous section](#3.-cursorless-tests-for-neovim). Nevertheless, we run +This is supported on Linux only, both locally and on CI. + +Many of the cursorless.nvim lua functions are run in order to complete Cursorless actions and so are already +indirectly tested by the tests described in the [previous section](#3-cursorless-tests-for-neovim). Nevertheless, we run more specific unit tests in order to give better visibility into exactly which functions are failing. The [busted](https://github.com/lunarmodules/busted) framework is used to test lua functions defined in cursorless.nvim. -This relies on a `./cursorless.nvim/.busted` file which directs busted to use a shell wrapper -`.cursorless.nvim/test/nvim-shim.sh` as its lua interpreter. It also declares that test specifications files are in -`./cursorless.nvim/test/unit/`. Any file in that folder ending with `_spec.lua` contains tests and will be executed -by neovim's lua interpreter, which allows neovim's internal API to be exposed to the tests. +This relies on a `cursorless.nvim/.busted` file which directs busted to use a lua interpreter and test specifications files: -The `nvim-shim.sh` script sets up an enclosed neovim environment by using [XDG Base +```bash +return { + _all = { + lua = './test/nvim-shim.sh' + }, + unit = { + ROOT = {'./test/unit/'}, + }, +} +``` + +The `.busted` file declares the `cursorless.nvim/test/nvim-shim.sh` shell wrapper as its lua interpreter. This script sets up an enclosed neovim environment by using [XDG Base Directory](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) environment variables (Linux -only) pointing to a temp directory. This allows loading any helpers and (optional) plugins needed to run -the tests. +only) pointing to a temp directory. This allows loading cursorless.nvim, any helpers and (optional) plugins needed to run +the tests. Consequently, the cursorless.nvim lua functions are exposed to the tests. Afterwards, the shim will use `nvim -l ` for each of the [lua test specifications scripts](https://neovim.io/doc/user/starting.html#-l). + +The `.busted` file declares that test specifications files are in +`cursorless.nvim/test/unit/`. Any file in that folder ending with `_spec.lua` contains tests and will be executed +by neovim's lua interpreter. -Afterwards, the shim will used `nvim` to execute whatever test spec file was passed by busted. Different tests rely on -the same custom helper functions. These functions are exposed as globals in a file called `helpers.lua` placed in `nvim/plugin/` insnide the isolated XDG environment. These helpers themselves also have their own unit tests that will be run by busted. +NOTE: Different tests rely on +the same custom test helper functions. These functions are exposed as globals in a file called `helpers.lua` placed in `nvim/plugin/` inside the isolated XDG environment. These helpers themselves also have their own unit tests that will be run by busted. This busted setup was inspired by this [blog post](https://hiphish.github.io/blog/2024/01/29/testing-neovim-plugins-with-busted/), which goes into greater detail. From d08005b2f4ad0b404230a55580acd67c517678fe Mon Sep 17 00:00:00 2001 From: fidgetingbits Date: Wed, 24 Jul 2024 21:03:19 +0800 Subject: [PATCH 12/14] refactor: print each test name as they run --- .vscode/settings.json | 3 +++ cursorless.nvim/.busted | 6 ++++-- cursorless.nvim/lua/cursorless/cursorless.lua | 4 ++-- cursorless.nvim/lua/cursorless/utils.lua | 2 +- cursorless.nvim/test/unit/cursorless_spec.lua | 9 --------- 5 files changed, 10 insertions(+), 14 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 04daeaa5d8..925d88c452 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -30,5 +30,8 @@ "Lua.diagnostics.globals": ["vim", "talon"], "[lua]": { "editor.defaultFormatter": "JohnnyMorganz.stylua" + }, + "files.associations": { + ".busted": "lua" } } diff --git a/cursorless.nvim/.busted b/cursorless.nvim/.busted index 6aaf242404..adf4c7b64d 100644 --- a/cursorless.nvim/.busted +++ b/cursorless.nvim/.busted @@ -1,8 +1,10 @@ return { _all = { - lua = './test/nvim-shim.sh' + lua = './test/nvim-shim.sh', + output = "TAP", + ["defer-print"] = false, }, unit = { ROOT = {'./test/unit/'}, }, -} + } diff --git a/cursorless.nvim/lua/cursorless/cursorless.lua b/cursorless.nvim/lua/cursorless/cursorless.lua index 36349c900c..b01f16de2a 100644 --- a/cursorless.nvim/lua/cursorless/cursorless.lua +++ b/cursorless.nvim/lua/cursorless/cursorless.lua @@ -54,9 +54,9 @@ end -- will highlight "llo" -- NOTE: works for any mode (n,i,v,nt) except in t mode function M.select_range(start_line, start_col, end_line, end_col) - vim.cmd([[normal! :noh]]) + vim.cmd([[silent! normal! :noh]]) vim.api.nvim_win_set_cursor(0, { start_line, start_col }) - vim.cmd([[normal v]]) + vim.cmd([[silent! normal v]]) vim.api.nvim_win_set_cursor(0, { end_line, end_col }) end diff --git a/cursorless.nvim/lua/cursorless/utils.lua b/cursorless.nvim/lua/cursorless/utils.lua index e0c091e9ef..cb9ca56718 100644 --- a/cursorless.nvim/lua/cursorless/utils.lua +++ b/cursorless.nvim/lua/cursorless/utils.lua @@ -58,7 +58,7 @@ end -- https://www.baeldung.com/linux/vim-paste-text -- e.g. run in command mode :imap lua require('cursorless.utils').paste() function M.paste() - vim.cmd([[normal! "+p]]) + vim.cmd([[silent! normal! "+p]]) end return M diff --git a/cursorless.nvim/test/unit/cursorless_spec.lua b/cursorless.nvim/test/unit/cursorless_spec.lua index c20e85b456..6474ada85d 100644 --- a/cursorless.nvim/test/unit/cursorless_spec.lua +++ b/cursorless.nvim/test/unit/cursorless_spec.lua @@ -60,15 +60,6 @@ describe("cursorless.nvim tests", function() local lines = "hello world" vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(lines, "\n")) cursorless.select_range(1, 4, 1, 2) - print( - table.concat( - _G.convert_table_entries( - cursorless.buffer_get_selection(), - tostring - ), - ", " - ) - ) assert( table.concat( _G.convert_table_entries( From 2f0f693672024f690bed26c4b43b6272d53f334a Mon Sep 17 00:00:00 2001 From: Cedric Halbronn Date: Thu, 25 Jul 2024 09:32:03 +0100 Subject: [PATCH 13/14] refactor: rename task --- .vscode/tasks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index d17eb70368..e4a18638d8 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -288,7 +288,7 @@ // so we need to show logs outside of vscode (see #2454) }, { - "label": "Neovim: Test Lua", + "label": "Neovim: Launch neovim (lua test)", "type": "shell", "command": "busted --run unit", "options": { From 5c9933478e36ad99f67a152e32ff01317ed88b73 Mon Sep 17 00:00:00 2001 From: Cedric Halbronn Date: Thu, 25 Jul 2024 10:43:55 +0100 Subject: [PATCH 14/14] refactor: better output for lua tests --- cursorless.nvim/test/unit/cursorless_spec.lua | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cursorless.nvim/test/unit/cursorless_spec.lua b/cursorless.nvim/test/unit/cursorless_spec.lua index 6474ada85d..1fff3d4495 100644 --- a/cursorless.nvim/test/unit/cursorless_spec.lua +++ b/cursorless.nvim/test/unit/cursorless_spec.lua @@ -1,8 +1,8 @@ -describe("cursorless.nvim tests", function() +describe("", function() local cursorless = require("cursorless.cursorless") - describe("window_get_visible_lines()", function() - it("Can read one visible line", function() + describe("window_get_visible_lines() ->", function() + it("can read one visible line", function() local pos = vim.api.nvim_win_get_cursor(0)[2] local line = vim.api.nvim_get_current_line() local nline = line:sub(0, pos) .. "hello" .. line:sub(pos + 1) @@ -12,7 +12,7 @@ describe("cursorless.nvim tests", function() assert(table.concat(visible) == table.concat({ 1, 1 })) end) - it("Can read all lines visible on the window", function() + it("can read all lines visible on the window", function() local maxlines = vim.api.nvim_win_get_height(0) local lines = {} for _ = 1, (maxlines + 1) do @@ -23,8 +23,8 @@ describe("cursorless.nvim tests", function() assert(table.concat(visible) == table.concat({ 1, maxlines })) end) end) - describe("select_range()", function() - it("Selects the specified range", function() + describe("select_range() ->", function() + it("selects the specified range", function() local lines = "hello world" vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(lines, "\n")) cursorless.select_range(1, 2, 1, 4) @@ -32,9 +32,9 @@ describe("cursorless.nvim tests", function() assert(table.concat(_G.get_selected_text()) == "llo") end) end) - describe("buffer_get_selection()", function() + describe("buffer_get_selection() ->", function() it( - "Can get the forward selection in a format expected by cursorless", + "can get the forward selection in a format expected by cursorless", function() local lines = "hello world" vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(lines, "\n")) @@ -55,7 +55,7 @@ describe("cursorless.nvim tests", function() end ) it( - "Can get the backward selection in a format expected by cursorless", + "can get the backward selection in a format expected by cursorless", function() local lines = "hello world" vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(lines, "\n"))