From f195f340c3b55ebba738773e81d0d0f3aaa2358f Mon Sep 17 00:00:00 2001 From: Mike Smith <10135646+mikesmithgh@users.noreply.github.com> Date: Tue, 5 Dec 2023 23:32:40 -0500 Subject: [PATCH] chore: create test harness --- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 42 +++ .github/workflows/vimdocs.yml | 2 +- .gitignore | 1 + CONTRIBUTING.md | 10 + Makefile | 12 + tests/helpers.lua | 247 ++++++++++++++++++ .../kitty_scrollback_spec.lua | 73 ++++++ tests/minimal_init.lua | 11 + tests/minimal_kitty.conf | 26 ++ 10 files changed, 424 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/test.yml create mode 100644 Makefile create mode 100644 tests/helpers.lua create mode 100644 tests/kitty-scrollback/kitty_scrollback_spec.lua create mode 100644 tests/minimal_init.lua create mode 100644 tests/minimal_kitty.conf diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c1f83c37..76840089 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ on: jobs: release: name: release - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 permissions: contents: write issues: write diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..b0252821 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,42 @@ +name: test +on: + pull_request: + branches: + - 'main' + workflow_dispatch: + schedule: + # Daily 2PM EST + - cron: "0 14 * * *" + +jobs: + plenary: + runs-on: ubuntu-22.04 + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Setup Neovim nightly + uses: rhysd/action-setup-vim@v1 + with: + neovim: true + version: nightly + + - name: Setup Kitty + run: | + curl -L https://sw.kovidgoyal.net/kitty/installer.sh | sh /dev/stdin launch=n + + - name: Install dependencies + run: | + sudo apt update + sudo apt install -y xfce4 libxcb-xkb1 + + - name: Test kitty-scrollback.nvim + run: | + Xvfb :1 -screen 0 1024x768x16 & + export PATH=$HOME/.local/kitty.app/bin:$PATH + export DISPLAY=:1 + echo "export PATH=$HOME/.local/kitty.app/bin:$PATH" >> "$HOME/.bashrc" + echo 'export DISPLAY=:1' >> "$HOME/.bashrc" + echo 'export PS1="\[\e[34m\]$ \[\e[m\]"' >> "$HOME/.bashrc" + make test + diff --git a/.github/workflows/vimdocs.yml b/.github/workflows/vimdocs.yml index d024d131..49b21329 100644 --- a/.github/workflows/vimdocs.yml +++ b/.github/workflows/vimdocs.yml @@ -13,7 +13,7 @@ on: jobs: vimdoc: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 name: generate vimdoc steps: - uses: actions/checkout@v2 diff --git a/.gitignore b/.gitignore index a5c74475..76481cd7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .worktrees/ .DS_Store tmp_vimdoc_workdir/ +tmp/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6e59f687..0f922bcf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,3 +12,13 @@ - `./scripts/make_video_markdown_files.sh` - `./scripts/make_adv_config_markdown_file.lua &> Advanced-Configuration.md` +## Troubleshooting + +### Github Actions +- Interactive debug the action by temporarily adding the build step +```yml +- name: Setup tmate session + uses: mxschmitt/action-tmate@v3 +``` +- Enable verbose logging by running the action with [debug logging](https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/enabling-debug-logging) enabled. + diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..95a4fb4b --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +TESTS_INIT=tests/minimal_init.lua +TESTS_DIR=tests/ + +.PHONY: test + +test: + @nvim \ + --headless \ + --noplugin \ + -u ${TESTS_INIT} \ + -c "PlenaryBustedDirectory ${TESTS_DIR} { minimal_init = '${TESTS_INIT}' }" + diff --git a/tests/helpers.lua b/tests/helpers.lua new file mode 100644 index 00000000..1d9594a4 --- /dev/null +++ b/tests/helpers.lua @@ -0,0 +1,247 @@ +local M = {} +local current_tmpsocket + +M.debug_enabled = vim.env.RUNNER_DEBUG == '1' +M.is_github_action = vim.env.GITHUB_ACTIONS == 'true' +--- @return any # given arguments. +M.debug = function(...) + if M.debug_enabled then + return vim.print(...) + end + return ... +end +M.debug({ + debug_enabled = M.debug_enabled, + is_github_action = M.is_github_action, +}) + +M.setup_backport = function() + if vim.fn.has('nvim-0.10') <= 0 then + -- vim.opt.runtimepath:append('/Users/mike/gitrepos/kitty-scrollback.nvim') -- TODO remove if not needed + require('kitty-scrollback.backport').setup() + end +end + +M.now = function() + return vim.fn.strftime('%m-%d-%Y %H:%M:%S', vim.fn.localtime()) +end + +M.tempsocket = function(tmp_dir) + local tmpdir = M.debug( + vim.system(vim.list_extend({ 'mktemp', '-d' }, tmp_dir and { '-p', tmp_dir } or {})):wait() + ).stdout + :gsub('\n', '') + current_tmpsocket = M.debug(tmpdir .. '/kitty-scrollback-nvim.sock') + return current_tmpsocket +end + +M.kitty_remote_cmd = function(tmpsock) + return { 'kitty', '@', '--to', 'unix:' .. (tmpsock or current_tmpsocket) } +end + +M.kitty_remote_get_text_cmd = function(args) + return vim.list_extend(M.kitty_remote_cmd(), vim.list_extend({ 'get-text' }, args or {})) +end + +M.kitty_remote_get_text = function(args, ...) + return M.debug(vim.system(M.debug(M.kitty_remote_get_text_cmd(args or {})), ...):wait()) +end + +M.kitty_remote_send_text_cmd = function(txt) + return vim.list_extend(M.kitty_remote_cmd(), { 'send-text', txt }) +end + +M.kitty_remote_send_text = function(txt, ...) + return M.debug(vim.system(M.kitty_remote_send_text_cmd(txt), ...):wait()) +end + +M.kitty_remote_set_title_cmd = function(title) + return vim.list_extend(M.kitty_remote_cmd(), { 'set-window-title', title }) +end + +M.kitty_remote_set_title = function(title, ...) + return vim.system(M.kitty_remote_set_title_cmd(title), ...) +end + +M.kitty_remote_close_window_cmd = function() + return vim.list_extend(M.kitty_remote_cmd(), { 'close-window' }) +end + +M.kitty_remote_close_window = function() + return M.debug(vim.system(M.kitty_remote_close_window_cmd()):wait()) +end + +M.kitty_remote_ls_cmd = function() + return vim.list_extend(M.kitty_remote_cmd(), { 'ls' }) +end + +M.kitty_remote_ls = function() + return vim.system(M.kitty_remote_ls_cmd()) +end + +M.kitty_remote_kitten_cmd = function() + return vim.list_extend(M.kitty_remote_cmd(), { 'kitten' }) +end + +M.kitty_remote_kitten_kitty_scrollback_nvim_cmd = function(ksb_args) + local kitty_scrollback_nvim_kitten = vim.fn.fnamemodify( + vim.api.nvim_get_runtime_file('python/kitty_scrollback_nvim.py', false)[1], + ':p' + ) + local kitten_args = vim.list_extend({ kitty_scrollback_nvim_kitten }, ksb_args or {}) + return vim.list_extend(M.kitty_remote_kitten_cmd(), kitten_args) +end + +M.kitty_remote_kitten_kitty_scrollback_nvim = function(ksb_args, ...) + return vim.system(M.kitty_remote_kitten_kitty_scrollback_nvim_cmd(ksb_args), ...) +end + +M.kitty_remote_kitten_kitty_scroll_prompt_cmd = function(direction, select_cmd_output) + local kitty_scroll_prompt_kitten = vim.fn.fnamemodify( + vim.api.nvim_get_runtime_file('python/kitty_scroll_prompt.py', false)[1], + ':p' + ) + local kitten_args = { + kitty_scroll_prompt_kitten, + direction or 0, + } + if select_cmd_output then + table.insert(kitten_args, 'true') + end + return vim.list_extend(M.kitty_remote_kitten_cmd(), kitten_args) +end + +M.kitty_remote_kitten_kitty_scroll_prompt = function(direction, select_cmd_output, ...) + return vim.system( + M.kitty_remote_kitten_kitty_scroll_prompt_cmd(direction, select_cmd_output), + ... + ) +end + +M.pause = function(delay) + vim.uv.sleep((delay or 0.5) * 1000) +end + +M.kitty_remote_kitten_kitty_scroll_prompt_and_pause = function(direction, select_cmd_output, ...) + local result = M.debug( + vim + .system(M.kitty_remote_kitten_kitty_scroll_prompt_cmd(direction, select_cmd_output), ...) + :wait() + ) + M.pause(1) + return result +end + +M.move_forward_one_prompt = function() + M.kitty_remote_kitten_kitty_scroll_prompt_and_pause(1) +end + +M.move_backward_one_prompt = function() + M.kitty_remote_kitten_kitty_scroll_prompt_and_pause(-1) +end + +M.move_to_first_prompt = function() + M.kitty_remote_kitten_kitty_scroll_prompt_and_pause(0) + M.kitty_remote_kitten_kitty_scroll_prompt_and_pause(-3) +end + +M.move_to_last_prompt = function() + M.kitty_remote_kitten_kitty_scroll_prompt_and_pause(0) + M.kitty_remote_kitten_kitty_scroll_prompt_and_pause(3) +end + +M.ksb = function(config_type, ksb_args, opts) + local o = opts or {} + M.kitty_remote_kitten_kitty_scrollback_nvim(ksb_args) + M.pause(o.before) + if o.show_text == nil or o.show_text then + M.kitty_remote_send_text([[a]]) + M.pause() + M.kitty_remote_send_text( + [[# ]] .. config_type .. [[ > kitty_scrollback_nvim ]] .. vim.fn.join(ksb_args or {}, ' ') + ) + M.kitty_remote_send_text([[\e0]]) + end + M.pause() + if o.msg then + M.pause() + M.kitty_remote_send_text([[o]]) + M.pause() + M.kitty_remote_send_text(o.msg) + M.kitty_remote_send_text([[\egg0]]) + end + M.pause(o.after or 4) + if not o.keep_open then + M.kitty_remote_close_window() + end + M.pause() +end + +M.ksb_b = function(...) + M.ksb('builtin', ...) +end + +M.ksb_e = function(...) + M.ksb('example', ...) +end + +M.ksb_builtin_last_visited_cmd_output_and_move_forward = function() + M.ksb_b({ '--config', 'ksb_builtin_last_visited_cmd_output' }, { + msg = [[ +default configuration for the mousemap `ctrl+shift+right` + +Show clicked command output in kitty-scrollback.nvim +]], + }) + M.move_forward_one_prompt() +end + +M.ksb_example_last_visited_cmd_output_plain_and_move_forward = function() + M.ksb_b( + { '--config', 'ksb_example_get_text_last_visited_cmd_output_plain' }, + { msg = [[ +Show clicked command plaintext output in kitty-scrollback.nvim +]] } + ) + M.move_forward_one_prompt() +end + +M.feed_kitty = function(input) + for _, line in pairs(input) do + if line == 'pause' then + M.pause() + elseif line == '__open_ksb' then + M.pause() + M.kitty_remote_kitten_kitty_scrollback_nvim() + M.pause() + elseif line:match('^\\') then + M.pause(0.2) + M.kitty_remote_send_text(line) + M.pause(0.2) + else + line:gsub('.', function(c) + M.kitty_remote_send_text(c) + M.pause(0.03) + end) + end + end + M.pause(3) -- longer pause for linux + + return M.debug(M.kitty_remote_get_text()).stdout +end + +M.assert_screen_equals = function(actual, expected, ...) + local actual_rstrip = actual:gsub('%s*\n', '\n') + local expected_rstrip = expected:gsub('%s*\n', '\n') + M.debug({ + actual = actual, + actual_rstrip = actual_rstrip, + actual_length = #actual, + expected = expected, + expected_rstrip = expected_rstrip, + expected_length = #expected, + }) + assert(actual_rstrip == expected_rstrip, ...) +end + +return M diff --git a/tests/kitty-scrollback/kitty_scrollback_spec.lua b/tests/kitty-scrollback/kitty_scrollback_spec.lua new file mode 100644 index 00000000..c47f1d32 --- /dev/null +++ b/tests/kitty-scrollback/kitty_scrollback_spec.lua @@ -0,0 +1,73 @@ +local h = require('tests.helpers') +local describe = describe ---@diagnostic disable-line: undefined-global +local it = it ---@diagnostic disable-line: undefined-global +local after_each = after_each ---@diagnostic disable-line: undefined-global +local before_each = before_each ---@diagnostic disable-line: undefined-global + +h.setup_backport() + +local ksb_dir = vim.fn.fnamemodify( + vim.fn.fnamemodify(vim.api.nvim_get_runtime_file('lua/kitty-scrollback', false)[1], ':h:h'), + ':p' +) +h.debug({ + ksb_dir = ksb_dir, + minimal_kitty_conf = ksb_dir .. 'tests/minimal_kitty.conf', +}) + +local tmpsock = h.tempsocket(ksb_dir .. 'tmp/') +local kitty_instance + +local shell = + h.debug(h.is_github_action and '/bin/bash' or (vim.o.shell .. ' --login --noprofile --norc')) + +local kitty_cmd = h.debug({ + 'kitty', + '--listen-on=unix:' .. tmpsock, + '--config', + ksb_dir .. 'tests/minimal_kitty.conf', + '--override', + 'shell=' .. shell, + '--session', + '-', -- read session from stdin +}) + +describe('kitty-scrollback.nvim', function() + before_each(function() + kitty_instance = vim.system(kitty_cmd, { + stdin = 'cd ' .. ksb_dir .. 'tests/workdir', + }) + local ready = false + vim.fn.wait(5000, function() + ready = (h.debug(h.kitty_remote_ls():wait()).code == 0) + return ready + end) + + if not ready then + vim.notify('kitty is not ready for remote connections, exiting', vim.log.levels.ERROR) + os.exit(1) -- TODO: change to an assert fail? + end + h.pause() + end) + + after_each(function() + kitty_instance:kill(2) + kitty_instance = nil + end) + + it('should show the terminal screen in nvim', function() + h.assert_screen_equals( + h.feed_kitty({ + [[echo meow]], + [[\n]], -- enter + [[__open_ksb]], + }), + [[ +$ echo meow 󰄛 󰣐  +meow +$ +]], + 'kitty-scrollback.nvim content did not match the terminal screen' + ) + end) +end) diff --git a/tests/minimal_init.lua b/tests/minimal_init.lua new file mode 100644 index 00000000..6d438146 --- /dev/null +++ b/tests/minimal_init.lua @@ -0,0 +1,11 @@ +local plenary_dir = os.getenv('PLENARY_DIR') or 'tmp/plenary.nvim' +local is_not_a_directory = vim.fn.isdirectory(plenary_dir) == 0 +if is_not_a_directory then + vim.fn.system({ 'git', 'clone', 'https://github.com/nvim-lua/plenary.nvim', plenary_dir }) +end + +vim.opt.rtp:append('.') +vim.opt.rtp:append(plenary_dir) + +vim.cmd('runtime plugin/plenary.vim') +require('plenary.busted') diff --git a/tests/minimal_kitty.conf b/tests/minimal_kitty.conf new file mode 100644 index 00000000..59702e42 --- /dev/null +++ b/tests/minimal_kitty.conf @@ -0,0 +1,26 @@ +# kitty-scrollback.nvim test configuration +enabled_layouts fat:bias=90 +remember_window_size no +initial_window_width 82c +initial_window_height 30c + +env PROMPT_COMMAND= +env PS1=\[\e[34m\]$ \[\e[m\] + +# kitty-scrollback.nvim required +allow_remote_control yes +listen_on unix:/tmp/kitty +shell_integration enabled + +# kitty-scrollback.nvim Kitten alias +action_alias kitty_scrollback_nvim kitten /home/runner/work/kitty-scrollback.nvim/kitty-scrollback.nvim/python/kitty_scrollback_nvim.py + +# Browse scrollback buffer in nvim +map kitty_mod+h kitty_scrollback_nvim +# Browse output of the last shell command in nvim +map kitty_mod+g kitty_scrollback_nvim --config ksb_builtin_last_cmd_output +# Show clicked command output in nvim +mouse_map kitty_mod+right press ungrabbed combine : mouse_select_command_output : kitty_scrollback_nvim --config ksb_builtin_last_visited_cmd_output + +# vim:fileencoding=utf-8:foldmethod=marker:filetype=kitty +