Skip to content

Commit

Permalink
test(suspend): fix failing tests
Browse files Browse the repository at this point in the history
Use bash to suspend/resume nvim process. The previous (manual) suspend
implementation was inadequate for some reason (perhaps because: term
settings, we were suspending session leader, and/or we weren't changing
the terminal's fg process group after sending `^Z`), and the process
would automatically resume on its own immediately after suspending.

Automating interactive bash may not be the best solution, but it works
for now and handles job control for us.

Resolves: #11
  • Loading branch information
tmillr committed Sep 22, 2024
1 parent e5d26c4 commit c4716bd
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 54 deletions.
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ Never manually save/write a buffer again!

This plugin is an autosaver for Neovim that automatically saves all of your changed buffers according to a predefined timeout value. Its main goals are:

- to handle conditions/situations that `'autowriteall'` does not
- to offer a complete, set-and-forget autosave/autowrite solution that saves your buffers for you when you want/need them saved
- to offer at least some customization via options, as well as the ability to easily enable/disable
- to be better or more correct than `CursorHold` autosavers and not depend on `CursorHold` if feasible
- To handle conditions/situations that `'autowriteall'` does not
- To offer a complete, set-and-forget autosave/autowrite solution that saves your buffers for you when you want/need them saved
- To offer at least some customization via options, as well as the ability to easily enable/disable
- To be better or more correct than `CursorHold` autosavers and not depend on `CursorHold` if feasible

### Additional Features

- has its own independent timer, distinct from `'updatetime'`, which may be set to any value in ms
- timer is only started/reset on buffer changes, not cursor movements or other irrelevant events
- keeps buffers in sync with the filesystem by frequently running `:checktime` in the background for you (e.g. on `CTRL-Z` or suspend, resume, command, etc.)
- intelligently ignores `'readonly'` and other such unwritable buffers/files (i.e. the writing of files with insufficient permissions must be attempted manually with `:w`)
- Has its own independent timer, distinct from `'updatetime'`, which may be set to any value in ms
- Timer is only started/reset on buffer changes, not cursor movements or other irrelevant events
- Keeps buffers in sync with the filesystem by frequently running `:checktime` in the background for you (e.g. on `CTRL-Z` or suspend, resume, command, etc.)
- Intelligently ignores `'readonly'` and other such unwritable buffers/files (i.e. the writing of files with insufficient permissions must be attempted manually with `:w`)

For any questions, help with setup, or general help, you can try [discussions][q&a]. For issues, bugs, apparent bugs, or feature requests, feel free to [open an issue][issues] or [create a pull request][prs].

Expand Down
38 changes: 23 additions & 15 deletions lua/sos/_test/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,10 @@ function M.bufwritemock(onwrite)
})
end

---@overload fun(nvim: table, file?: string): integer, string
---@overload fun(file?: string): integer, string
---@return integer bufnr
---@return string output
---@overload fun(nvim: table, file?: string)
---@overload fun(file?: string)
function M.silent_edit(...)
local external_nvim_or_api, file = M.nvim_recv_or_api(...)
local out = external_nvim_or_api.nvim_cmd({
Expand Down Expand Up @@ -222,7 +222,6 @@ function M.start_nvim(opts)
}

local sock_addr = M.tmpfile()

local args = {
'nvim',
'--clean',
Expand All @@ -239,23 +238,28 @@ function M.start_nvim(opts)
table.insert(args, 2, '-u')
end

local jobid = vim.fn.jobstart(args, job_opts)
local jobid = vim.fn.jobstart({ 'bash', '--norc', '--noprofile' }, job_opts)
assert(jobid > 0, 'ERROR: jobstart(): failed to start nvim process')
local chan
M.wait(100)

for i = 2, #args do
args[i] = "'" .. args[i]:gsub("'", "'\\''") .. "'"
end

assert(vim.fn.chansend(jobid, table.concat(args, ' ')) > 0)
assert(vim.fn.chansend(jobid, '\r') > 0)

do
local ok
M.wait(200)
local i, ok = 0, nil

for _ = 1, 4 do
repeat
M.wait(50)
ok, chan = pcall(
function() return vim.fn.sockconnect('pipe', sock_addr, { rpc = true }) end
)

if ok then break end
M.wait(500)
end

assert(ok, chan)
i = i + 1
until ok or (i == 20 and assert(ok, chan))
end

assert(
Expand All @@ -272,9 +276,13 @@ function M.start_nvim(opts)

function self:req(...) return vim.rpcrequest(self.chan, ...) end

function self:suspend() assert(self:input '<C-Z>' > 0) end
function self:suspend()
-- assert(self:input '<C-Z>' > 0)
assert(vim.fn.chansend(jobid, '\26') > 0)
end

function self:cont() M.kill(self.pid, 'SIGCONT') end
-- function self:cont() M.kill(self.pid, 'SIGCONT') end
function self:cont() assert(vim.fn.chansend(jobid, 'fg\r') > 0) end

function self:stop() vim.fn.jobstop(jobid) end

Expand Down
46 changes: 15 additions & 31 deletions tests/suspend_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe('test harness', function()
})

nvim:suspend()
util.wait(500)
util.wait(250)
res.got_VimSuspend = util.file_exists(got_suspend)
got_VimResume_before_resuming = util.file_exists(got_resume)

Expand All @@ -33,7 +33,7 @@ describe('test harness', function()
-- assert(out:find "^T", "ps output: " .. out)

nvim:cont()
util.wait(500)
util.wait(250)
res.got_VimResume = util.file_exists(got_resume)
got_VimSuspend_after_resuming = not res.got_VimSuspend
and util.file_exists(got_suspend)
Expand All @@ -47,19 +47,14 @@ describe('test harness', function()
end)

describe('VimSuspend and VimResume', function()
it('fire at the correct and incorrect time, respectively', function()
it('fire at the correct time', function()
assert.is.False(
got_VimSuspend_after_resuming,
'incorrectly got VimSuspend after resuming, expected before'
'expected VimSuspend before resuming'
)
-- TODO: This isn't the behavior when actually running/using nvim normally
-- in a normal terminal. Why?
--
-- Maybe this test should only be run manually (or, we need to upgrade the
-- test harness to be able to handle normal/realistic suspend/resume).
assert.is.True(
assert.is.False(
got_VimResume_before_resuming,
'correctly got VimResume after resuming, expected before'
'expected VimResume after resuming'
)
end)
end)
Expand All @@ -68,31 +63,29 @@ describe('neovim by default', function()
-- NOTE: However, 'autowriteall' implies 'autowrite'!
it("doesn't save on suspend when 'autowrite' is off", function()
local nvim = util.start_nvim()
local tmp = vim.fn.tempname()
local tmp = util.tmpfile()
nvim:set_option('autowrite', false)
nvim:set_option('autowriteall', false)
nvim:buf_set_name(0, tmp)
nvim:buf_set_lines(0, 0, -1, true, { 'x' })
nvim:suspend()
util.wait(500)
assert(vim.loop.fs_stat(tmp) == nil, 'expected file not to be saved')
vim.fn.delete(tmp)
end)

it(
"does save on suspend when 'autowrite' is on, even if &bufhidden = hide",
function()
local nvim = util.start_nvim()
local tmp = vim.fn.tempname()
local tmp = util.tmpfile()
nvim:buf_set_option(0, 'bufhidden', 'hide')
nvim:set_option('autowrite', true)
nvim:buf_set_name(0, tmp)
nvim:buf_set_lines(0, 0, -1, true, { 'x' })
nvim:suspend()
util.wait(500)
util.wait(250)
local stat = assert(vim.loop.fs_stat(tmp))
assert(stat.type == 'file', "dirent exists but isn't a regular file")
vim.fn.delete(tmp)
end
)

Expand All @@ -112,7 +105,7 @@ describe('neovim by default', function()
-- the checking of file times on vim resume.
it("doesn't do `:checktime` nor autoread on resume", function()
local nvim = util.start_nvim()
local tmp = vim.fn.tempname()
local tmp = util.tmpfile()
assert(vim.fn.writefile({ 'old' }, tmp, 'b') == 0)
nvim:set_option('autoread', true)
nvim:cmd({ cmd = 'edit', args = { tmp } }, { output = false })
Expand All @@ -128,12 +121,11 @@ describe('neovim by default', function()
assert(
table.concat(nvim:buf_get_lines(0, 0, -1, true), '') == 'new new new'
)
vim.fn.delete(tmp)
end)

it("doesn't automatically check file times upon leaving term", function()
local nvim = util.start_nvim {}
local tmp = vim.fn.tempname()
local tmp = util.tmpfile()
assert(vim.fn.writefile({ 'old' }, tmp, 'b') == 0)
nvim:set_option('autoread', true)

Expand All @@ -156,30 +148,22 @@ describe('neovim by default', function()
nvim:set_current_tabpage(tab) -- trigger sos to check file times (which triggers autoread)

assert(table.concat(nvim:buf_get_lines(buf, 0, -1, true), '') == 'old')

vim.fn.delete(tmp)
end)

it('fires UIEnter on resume', function()
util.with_nvim(function(nvim)
local got_UIEnter = util.tmpfile()

nvim:create_autocmd('UIEnter', {
once = true,
nested = false,
command = ([[lua vim.fn.writefile({}, %q, 's')]]):format(got_UIEnter),
})

nvim:suspend()
util.wait(500)
-- TODO: This line will fail when testing, but this isn't the behavior
-- when actually running/using nvim normally in a normal terminal. Why?
--
-- Maybe this test should only be run manually (or, we need to upgrade the
-- test harness to be able to handle normal/realistic suspend/resume).
-- assert.is.False(util.file_exists(got_UIEnter))
util.wait(250)
assert.is.False(util.file_exists(got_UIEnter))
nvim:cont()
util.wait(500)
util.wait(250)
assert.is.True(util.file_exists(got_UIEnter))
end)
end)
Expand All @@ -197,7 +181,7 @@ describe('sos.nvim', function()
-- Maybe these kinds of tests should only be run manually (or, we need to
-- upgrade the test harness to be able to handle normal/realistic
-- suspend/resume).
pending('should automatically check file times on resume', function()
it('should automatically check file times on resume', function()
local nvim = util.start_nvim {
xargs = {
'-u',
Expand Down

0 comments on commit c4716bd

Please sign in to comment.