build: migrate test framework from plenary to busted
Problem: plenary.nvim is deprecated. The test suite depends on plenary's async test runner and coroutine-based utilities, tying the project to an unmaintained dependency. CI also tests against Neovim 0.8-0.11, which are no longer relevant. Solution: replace plenary with busted + nlua (nvim -l). Convert all async test patterns (a.wrap, a.util.sleep, a.util.scheduler) to synchronous equivalents using vim.wait. Rename tests/ to spec/ to follow busted convention. Replace the CI test matrix with nvim-busted-action targeting stable/nightly only. Add .busted config, luarocks test_dependencies, and update the nix devshell.
This commit is contained in:
parent
a4da206b67
commit
6be0148eef
28 changed files with 257 additions and 298 deletions
151
spec/altbuf_spec.lua
Normal file
151
spec/altbuf_spec.lua
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
local fs = require('oil.fs')
|
||||
local oil = require('oil')
|
||||
local test_util = require('spec.test_util')
|
||||
|
||||
describe('Alternate buffer', function()
|
||||
after_each(function()
|
||||
test_util.reset_editor()
|
||||
end)
|
||||
|
||||
it('sets previous buffer as alternate', function()
|
||||
vim.cmd.edit({ args = { 'foo' } })
|
||||
oil.open()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
vim.cmd.edit({ args = { 'bar' } })
|
||||
assert.equals('foo', vim.fn.expand('#'))
|
||||
end)
|
||||
|
||||
it('sets previous buffer as alternate when editing url file', function()
|
||||
vim.cmd.edit({ args = { 'foo' } })
|
||||
oil.open()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
local readme = fs.join(vim.fn.getcwd(), 'README.md')
|
||||
vim.cmd.edit({ args = { 'oil://' .. fs.os_to_posix_path(readme) } })
|
||||
test_util.wait_for_autocmd('BufEnter')
|
||||
test_util.wait_for_autocmd('BufEnter')
|
||||
assert.equals(readme, vim.api.nvim_buf_get_name(0))
|
||||
assert.equals('foo', vim.fn.expand('#'))
|
||||
end)
|
||||
|
||||
it('sets previous buffer as alternate when editing oil://', function()
|
||||
vim.cmd.edit({ args = { 'foo' } })
|
||||
vim.cmd.edit({ args = { 'oil://' .. fs.os_to_posix_path(vim.fn.getcwd()) } })
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
vim.cmd.edit({ args = { 'bar' } })
|
||||
assert.equals('foo', vim.fn.expand('#'))
|
||||
end)
|
||||
|
||||
it('preserves alternate buffer if editing the same file', function()
|
||||
vim.cmd.edit({ args = { 'foo' } })
|
||||
vim.cmd.edit({ args = { 'bar' } })
|
||||
oil.open()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
vim.cmd.edit({ args = { 'bar' } })
|
||||
assert.equals('foo', vim.fn.expand('#'))
|
||||
end)
|
||||
|
||||
it('preserves alternate buffer if discarding changes', function()
|
||||
vim.cmd.edit({ args = { 'foo' } })
|
||||
vim.cmd.edit({ args = { 'bar' } })
|
||||
oil.open()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
oil.close()
|
||||
assert.equals('bar', vim.fn.expand('%'))
|
||||
assert.equals('foo', vim.fn.expand('#'))
|
||||
end)
|
||||
|
||||
it('sets previous buffer as alternate after multi-dir hops', function()
|
||||
vim.cmd.edit({ args = { 'foo' } })
|
||||
oil.open()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
oil.open()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
oil.open()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
oil.open()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
vim.cmd.edit({ args = { 'bar' } })
|
||||
assert.equals('foo', vim.fn.expand('#'))
|
||||
end)
|
||||
|
||||
it('sets previous buffer as alternate when inside oil buffer', function()
|
||||
vim.cmd.edit({ args = { 'foo' } })
|
||||
oil.open()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
assert.equals('foo', vim.fn.expand('#'))
|
||||
vim.cmd.edit({ args = { 'bar' } })
|
||||
assert.equals('foo', vim.fn.expand('#'))
|
||||
oil.open()
|
||||
assert.equals('bar', vim.fn.expand('#'))
|
||||
end)
|
||||
|
||||
it('preserves alternate when traversing oil dirs', function()
|
||||
vim.cmd.edit({ args = { 'foo' } })
|
||||
oil.open()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
assert.equals('foo', vim.fn.expand('#'))
|
||||
vim.wait(1000, function()
|
||||
return oil.get_cursor_entry()
|
||||
end, 10)
|
||||
vim.api.nvim_win_set_cursor(0, { 1, 1 })
|
||||
oil.select()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
assert.equals('foo', vim.fn.expand('#'))
|
||||
end)
|
||||
|
||||
it('preserves alternate when opening preview', function()
|
||||
vim.cmd.edit({ args = { 'foo' } })
|
||||
oil.open()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
assert.equals('foo', vim.fn.expand('#'))
|
||||
vim.wait(1000, function()
|
||||
return oil.get_cursor_entry()
|
||||
end, 10)
|
||||
vim.api.nvim_win_set_cursor(0, { 1, 1 })
|
||||
oil.open_preview()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
assert.equals('foo', vim.fn.expand('#'))
|
||||
end)
|
||||
|
||||
describe('floating window', function()
|
||||
it('sets previous buffer as alternate', function()
|
||||
vim.cmd.edit({ args = { 'foo' } })
|
||||
oil.open_float()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
vim.api.nvim_win_close(0, true)
|
||||
vim.cmd.edit({ args = { 'bar' } })
|
||||
assert.equals('foo', vim.fn.expand('#'))
|
||||
end)
|
||||
|
||||
it('preserves alternate buffer if editing the same file', function()
|
||||
vim.cmd.edit({ args = { 'foo' } })
|
||||
vim.cmd.edit({ args = { 'bar' } })
|
||||
oil.open_float()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
vim.api.nvim_win_close(0, true)
|
||||
vim.cmd.edit({ args = { 'bar' } })
|
||||
assert.equals('foo', vim.fn.expand('#'))
|
||||
end)
|
||||
|
||||
it('preserves alternate buffer if discarding changes', function()
|
||||
vim.cmd.edit({ args = { 'foo' } })
|
||||
vim.cmd.edit({ args = { 'bar' } })
|
||||
oil.open_float()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
oil.close()
|
||||
assert.equals('foo', vim.fn.expand('#'))
|
||||
end)
|
||||
|
||||
it('preserves alternate when traversing to a new file', function()
|
||||
vim.cmd.edit({ args = { 'foo' } })
|
||||
oil.open_float()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
assert.equals('foo', vim.fn.expand('#'))
|
||||
test_util.feedkeys({ '/LICENSE<CR>' }, 10)
|
||||
oil.select()
|
||||
test_util.wait_for_autocmd('BufEnter')
|
||||
assert.equals('LICENSE', vim.fn.expand('%:.'))
|
||||
assert.equals('foo', vim.fn.expand('#'))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
44
spec/close_spec.lua
Normal file
44
spec/close_spec.lua
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
local oil = require('oil')
|
||||
local test_util = require('spec.test_util')
|
||||
|
||||
describe('close', function()
|
||||
before_each(function()
|
||||
test_util.reset_editor()
|
||||
end)
|
||||
after_each(function()
|
||||
test_util.reset_editor()
|
||||
end)
|
||||
|
||||
it('does not close buffer from visual mode', function()
|
||||
oil.open()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
assert.equals('oil', vim.bo.filetype)
|
||||
test_util.feedkeys({ 'V' }, 10)
|
||||
oil.close()
|
||||
assert.equals('oil', vim.bo.filetype)
|
||||
test_util.feedkeys({ '<Esc>' }, 10)
|
||||
end)
|
||||
|
||||
it('does not close buffer from operator-pending mode', function()
|
||||
oil.open()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
assert.equals('oil', vim.bo.filetype)
|
||||
vim.api.nvim_feedkeys('d', 'n', false)
|
||||
vim.wait(20)
|
||||
local mode = vim.api.nvim_get_mode().mode
|
||||
if mode:match('^no') then
|
||||
oil.close()
|
||||
assert.equals('oil', vim.bo.filetype)
|
||||
end
|
||||
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('<Esc>', true, true, true), 'n', false)
|
||||
vim.wait(20)
|
||||
end)
|
||||
|
||||
it('closes buffer from normal mode', function()
|
||||
oil.open()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
assert.equals('oil', vim.bo.filetype)
|
||||
oil.close()
|
||||
assert.not_equals('oil', vim.bo.filetype)
|
||||
end)
|
||||
end)
|
||||
27
spec/config_spec.lua
Normal file
27
spec/config_spec.lua
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
local config = require('oil.config')
|
||||
|
||||
describe('config', function()
|
||||
after_each(function()
|
||||
vim.g.oil = nil
|
||||
end)
|
||||
|
||||
it('falls back to vim.g.oil when setup() is called with no args', function()
|
||||
vim.g.oil = { delete_to_trash = true, cleanup_delay_ms = 5000 }
|
||||
config.setup()
|
||||
assert.is_true(config.delete_to_trash)
|
||||
assert.equals(5000, config.cleanup_delay_ms)
|
||||
end)
|
||||
|
||||
it('uses defaults when neither opts nor vim.g.oil is set', function()
|
||||
vim.g.oil = nil
|
||||
config.setup()
|
||||
assert.is_false(config.delete_to_trash)
|
||||
assert.equals(2000, config.cleanup_delay_ms)
|
||||
end)
|
||||
|
||||
it('prefers explicit opts over vim.g.oil', function()
|
||||
vim.g.oil = { delete_to_trash = true }
|
||||
config.setup({ delete_to_trash = false })
|
||||
assert.is_false(config.delete_to_trash)
|
||||
end)
|
||||
end)
|
||||
166
spec/files_spec.lua
Normal file
166
spec/files_spec.lua
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
local TmpDir = require('spec.tmpdir')
|
||||
local files = require('oil.adapters.files')
|
||||
local test_util = require('spec.test_util')
|
||||
|
||||
describe('files adapter', function()
|
||||
local tmpdir
|
||||
before_each(function()
|
||||
tmpdir = TmpDir.new()
|
||||
end)
|
||||
after_each(function()
|
||||
if tmpdir then
|
||||
tmpdir:dispose()
|
||||
end
|
||||
test_util.reset_editor()
|
||||
end)
|
||||
|
||||
it('tmpdir creates files and asserts they exist', function()
|
||||
tmpdir:create({ 'a.txt', 'foo/b.txt', 'foo/c.txt', 'bar/' })
|
||||
tmpdir:assert_fs({
|
||||
['a.txt'] = 'a.txt',
|
||||
['foo/b.txt'] = 'foo/b.txt',
|
||||
['foo/c.txt'] = 'foo/c.txt',
|
||||
['bar/'] = true,
|
||||
})
|
||||
end)
|
||||
|
||||
it('Creates files', function()
|
||||
local err = test_util.await(files.perform_action, 2, {
|
||||
url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a.txt',
|
||||
entry_type = 'file',
|
||||
type = 'create',
|
||||
})
|
||||
assert.is_nil(err)
|
||||
tmpdir:assert_fs({
|
||||
['a.txt'] = '',
|
||||
})
|
||||
end)
|
||||
|
||||
it('Creates directories', function()
|
||||
local err = test_util.await(files.perform_action, 2, {
|
||||
url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a',
|
||||
entry_type = 'directory',
|
||||
type = 'create',
|
||||
})
|
||||
assert.is_nil(err)
|
||||
tmpdir:assert_fs({
|
||||
['a/'] = true,
|
||||
})
|
||||
end)
|
||||
|
||||
it('Deletes files', function()
|
||||
tmpdir:create({ 'a.txt' })
|
||||
local url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a.txt'
|
||||
local err = test_util.await(files.perform_action, 2, {
|
||||
url = url,
|
||||
entry_type = 'file',
|
||||
type = 'delete',
|
||||
})
|
||||
assert.is_nil(err)
|
||||
tmpdir:assert_fs({})
|
||||
end)
|
||||
|
||||
it('Deletes directories', function()
|
||||
tmpdir:create({ 'a/' })
|
||||
local url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a'
|
||||
local err = test_util.await(files.perform_action, 2, {
|
||||
url = url,
|
||||
entry_type = 'directory',
|
||||
type = 'delete',
|
||||
})
|
||||
assert.is_nil(err)
|
||||
tmpdir:assert_fs({})
|
||||
end)
|
||||
|
||||
it('Moves files', function()
|
||||
tmpdir:create({ 'a.txt' })
|
||||
local src_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a.txt'
|
||||
local dest_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'b.txt'
|
||||
local err = test_util.await(files.perform_action, 2, {
|
||||
src_url = src_url,
|
||||
dest_url = dest_url,
|
||||
entry_type = 'file',
|
||||
type = 'move',
|
||||
})
|
||||
assert.is_nil(err)
|
||||
tmpdir:assert_fs({
|
||||
['b.txt'] = 'a.txt',
|
||||
})
|
||||
end)
|
||||
|
||||
it('Moves directories', function()
|
||||
tmpdir:create({ 'a/a.txt' })
|
||||
local src_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a'
|
||||
local dest_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'b'
|
||||
local err = test_util.await(files.perform_action, 2, {
|
||||
src_url = src_url,
|
||||
dest_url = dest_url,
|
||||
entry_type = 'directory',
|
||||
type = 'move',
|
||||
})
|
||||
assert.is_nil(err)
|
||||
tmpdir:assert_fs({
|
||||
['b/a.txt'] = 'a/a.txt',
|
||||
['b/'] = true,
|
||||
})
|
||||
end)
|
||||
|
||||
it('Copies files', function()
|
||||
tmpdir:create({ 'a.txt' })
|
||||
local src_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a.txt'
|
||||
local dest_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'b.txt'
|
||||
local err = test_util.await(files.perform_action, 2, {
|
||||
src_url = src_url,
|
||||
dest_url = dest_url,
|
||||
entry_type = 'file',
|
||||
type = 'copy',
|
||||
})
|
||||
assert.is_nil(err)
|
||||
tmpdir:assert_fs({
|
||||
['a.txt'] = 'a.txt',
|
||||
['b.txt'] = 'a.txt',
|
||||
})
|
||||
end)
|
||||
|
||||
it('Recursively copies directories', function()
|
||||
tmpdir:create({ 'a/a.txt' })
|
||||
local src_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a'
|
||||
local dest_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'b'
|
||||
local err = test_util.await(files.perform_action, 2, {
|
||||
src_url = src_url,
|
||||
dest_url = dest_url,
|
||||
entry_type = 'directory',
|
||||
type = 'copy',
|
||||
})
|
||||
assert.is_nil(err)
|
||||
tmpdir:assert_fs({
|
||||
['b/a.txt'] = 'a/a.txt',
|
||||
['b/'] = true,
|
||||
['a/a.txt'] = 'a/a.txt',
|
||||
['a/'] = true,
|
||||
})
|
||||
end)
|
||||
|
||||
it('Editing a new oil://path/ creates an oil buffer', function()
|
||||
local tmpdir_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. '/'
|
||||
vim.cmd.edit({ args = { tmpdir_url } })
|
||||
test_util.wait_oil_ready()
|
||||
local new_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'newdir'
|
||||
vim.cmd.edit({ args = { new_url } })
|
||||
test_util.wait_oil_ready()
|
||||
assert.equals('oil', vim.bo.filetype)
|
||||
assert.equals(new_url .. '/', vim.api.nvim_buf_get_name(0))
|
||||
end)
|
||||
|
||||
it('Editing a new oil://file.rb creates a normal buffer', function()
|
||||
local tmpdir_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. '/'
|
||||
vim.cmd.edit({ args = { tmpdir_url } })
|
||||
test_util.wait_for_autocmd('BufReadPost')
|
||||
local new_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'file.rb'
|
||||
vim.cmd.edit({ args = { new_url } })
|
||||
test_util.wait_for_autocmd('BufReadPost')
|
||||
assert.equals('ruby', vim.bo.filetype)
|
||||
assert.equals(vim.fn.fnamemodify(tmpdir.path, ':p') .. 'file.rb', vim.api.nvim_buf_get_name(0))
|
||||
assert.equals(tmpdir.path .. '/file.rb', vim.fn.bufname())
|
||||
end)
|
||||
end)
|
||||
28
spec/manual_progress.lua
Normal file
28
spec/manual_progress.lua
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
-- Manual test for minimizing/restoring progress window
|
||||
local Progress = require('oil.mutator.progress')
|
||||
|
||||
local progress = Progress.new()
|
||||
|
||||
progress:show({
|
||||
cancel = function()
|
||||
progress:close()
|
||||
end,
|
||||
})
|
||||
|
||||
for i = 1, 10, 1 do
|
||||
vim.defer_fn(function()
|
||||
progress:set_action({
|
||||
type = 'create',
|
||||
url = string.format('oil:///tmp/test_%d.txt', i),
|
||||
entry_type = 'file',
|
||||
}, i, 10)
|
||||
end, (i - 1) * 1000)
|
||||
end
|
||||
|
||||
vim.defer_fn(function()
|
||||
progress:close()
|
||||
end, 10000)
|
||||
|
||||
vim.keymap.set('n', 'R', function()
|
||||
progress:restore()
|
||||
end, {})
|
||||
8
spec/minimal_init.lua
Normal file
8
spec/minimal_init.lua
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
vim.cmd([[set runtimepath=$VIMRUNTIME]])
|
||||
vim.opt.runtimepath:append('.')
|
||||
vim.opt.packpath = {}
|
||||
vim.o.swapfile = false
|
||||
vim.fn.mkdir(vim.fn.stdpath('cache'), 'p')
|
||||
vim.fn.mkdir(vim.fn.stdpath('data'), 'p')
|
||||
vim.fn.mkdir(vim.fn.stdpath('state'), 'p')
|
||||
require('spec.test_util').reset_editor()
|
||||
59
spec/move_rename_spec.lua
Normal file
59
spec/move_rename_spec.lua
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
local fs = require('oil.fs')
|
||||
local test_util = require('spec.test_util')
|
||||
local util = require('oil.util')
|
||||
|
||||
describe('update_moved_buffers', function()
|
||||
after_each(function()
|
||||
test_util.reset_editor()
|
||||
end)
|
||||
|
||||
it('Renames moved buffers', function()
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/bar.txt' } })
|
||||
util.update_moved_buffers('file', 'oil-test:///foo/bar.txt', 'oil-test:///foo/baz.txt')
|
||||
assert.equals('oil-test:///foo/baz.txt', vim.api.nvim_buf_get_name(0))
|
||||
end)
|
||||
|
||||
it('Renames moved buffers when they are normal files', function()
|
||||
local tmpdir = fs.join(vim.loop.fs_realpath(vim.fn.stdpath('cache')), 'oil', 'test')
|
||||
local testfile = fs.join(tmpdir, 'foo.txt')
|
||||
vim.cmd.edit({ args = { testfile } })
|
||||
util.update_moved_buffers(
|
||||
'file',
|
||||
'oil://' .. fs.os_to_posix_path(testfile),
|
||||
'oil://' .. fs.os_to_posix_path(fs.join(tmpdir, 'bar.txt'))
|
||||
)
|
||||
assert.equals(fs.join(tmpdir, 'bar.txt'), vim.api.nvim_buf_get_name(0))
|
||||
end)
|
||||
|
||||
it('Renames directories', function()
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
||||
util.update_moved_buffers('directory', 'oil-test:///foo/', 'oil-test:///bar/')
|
||||
assert.equals('oil-test:///bar/', vim.api.nvim_buf_get_name(0))
|
||||
end)
|
||||
|
||||
it('Renames subdirectories', function()
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/bar/' } })
|
||||
util.update_moved_buffers('directory', 'oil-test:///foo/', 'oil-test:///baz/')
|
||||
assert.equals('oil-test:///baz/bar/', vim.api.nvim_buf_get_name(0))
|
||||
end)
|
||||
|
||||
it('Renames subfiles', function()
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/bar.txt' } })
|
||||
util.update_moved_buffers('directory', 'oil-test:///foo/', 'oil-test:///baz/')
|
||||
assert.equals('oil-test:///baz/bar.txt', vim.api.nvim_buf_get_name(0))
|
||||
end)
|
||||
|
||||
it('Renames subfiles when they are normal files', function()
|
||||
local tmpdir = fs.join(vim.loop.fs_realpath(vim.fn.stdpath('cache')), 'oil', 'test')
|
||||
local foo = fs.join(tmpdir, 'foo')
|
||||
local bar = fs.join(tmpdir, 'bar')
|
||||
local testfile = fs.join(foo, 'foo.txt')
|
||||
vim.cmd.edit({ args = { testfile } })
|
||||
util.update_moved_buffers(
|
||||
'directory',
|
||||
'oil://' .. fs.os_to_posix_path(foo),
|
||||
'oil://' .. fs.os_to_posix_path(bar)
|
||||
)
|
||||
assert.equals(fs.join(bar, 'foo.txt'), vim.api.nvim_buf_get_name(0))
|
||||
end)
|
||||
end)
|
||||
419
spec/mutator_spec.lua
Normal file
419
spec/mutator_spec.lua
Normal file
|
|
@ -0,0 +1,419 @@
|
|||
local cache = require('oil.cache')
|
||||
local constants = require('oil.constants')
|
||||
local mutator = require('oil.mutator')
|
||||
local test_adapter = require('oil.adapters.test')
|
||||
local test_util = require('spec.test_util')
|
||||
|
||||
local FIELD_ID = constants.FIELD_ID
|
||||
local FIELD_NAME = constants.FIELD_NAME
|
||||
local FIELD_TYPE = constants.FIELD_TYPE
|
||||
|
||||
describe('mutator', function()
|
||||
after_each(function()
|
||||
test_util.reset_editor()
|
||||
end)
|
||||
|
||||
describe('build actions', function()
|
||||
it('empty diffs produce no actions', function()
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local actions = mutator.create_actions_from_diffs({
|
||||
[bufnr] = {},
|
||||
})
|
||||
assert.are.same({}, actions)
|
||||
end)
|
||||
|
||||
it('constructs CREATE actions', function()
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local diffs = {
|
||||
{ type = 'new', name = 'a.txt', entry_type = 'file' },
|
||||
}
|
||||
local actions = mutator.create_actions_from_diffs({
|
||||
[bufnr] = diffs,
|
||||
})
|
||||
assert.are.same({
|
||||
{
|
||||
type = 'create',
|
||||
entry_type = 'file',
|
||||
url = 'oil-test:///foo/a.txt',
|
||||
},
|
||||
}, actions)
|
||||
end)
|
||||
|
||||
it('constructs DELETE actions', function()
|
||||
local file = test_adapter.test_set('/foo/a.txt', 'file')
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local diffs = {
|
||||
{ type = 'delete', name = 'a.txt', id = file[FIELD_ID] },
|
||||
}
|
||||
local actions = mutator.create_actions_from_diffs({
|
||||
[bufnr] = diffs,
|
||||
})
|
||||
assert.are.same({
|
||||
{
|
||||
type = 'delete',
|
||||
entry_type = 'file',
|
||||
url = 'oil-test:///foo/a.txt',
|
||||
},
|
||||
}, actions)
|
||||
end)
|
||||
|
||||
it('constructs COPY actions', function()
|
||||
local file = test_adapter.test_set('/foo/a.txt', 'file')
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local diffs = {
|
||||
{ type = 'new', name = 'b.txt', entry_type = 'file', id = file[FIELD_ID] },
|
||||
}
|
||||
local actions = mutator.create_actions_from_diffs({
|
||||
[bufnr] = diffs,
|
||||
})
|
||||
assert.are.same({
|
||||
{
|
||||
type = 'copy',
|
||||
entry_type = 'file',
|
||||
src_url = 'oil-test:///foo/a.txt',
|
||||
dest_url = 'oil-test:///foo/b.txt',
|
||||
},
|
||||
}, actions)
|
||||
end)
|
||||
|
||||
it('constructs MOVE actions', function()
|
||||
local file = test_adapter.test_set('/foo/a.txt', 'file')
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local diffs = {
|
||||
{ type = 'delete', name = 'a.txt', id = file[FIELD_ID] },
|
||||
{ type = 'new', name = 'b.txt', entry_type = 'file', id = file[FIELD_ID] },
|
||||
}
|
||||
local actions = mutator.create_actions_from_diffs({
|
||||
[bufnr] = diffs,
|
||||
})
|
||||
assert.are.same({
|
||||
{
|
||||
type = 'move',
|
||||
entry_type = 'file',
|
||||
src_url = 'oil-test:///foo/a.txt',
|
||||
dest_url = 'oil-test:///foo/b.txt',
|
||||
},
|
||||
}, actions)
|
||||
end)
|
||||
|
||||
it('correctly orders MOVE + CREATE', function()
|
||||
local file = test_adapter.test_set('/a.txt', 'file')
|
||||
vim.cmd.edit({ args = { 'oil-test:///' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local diffs = {
|
||||
{ type = 'delete', name = 'a.txt', id = file[FIELD_ID] },
|
||||
{ type = 'new', name = 'b.txt', entry_type = 'file', id = file[FIELD_ID] },
|
||||
{ type = 'new', name = 'a.txt', entry_type = 'file' },
|
||||
}
|
||||
local actions = mutator.create_actions_from_diffs({
|
||||
[bufnr] = diffs,
|
||||
})
|
||||
assert.are.same({
|
||||
{
|
||||
type = 'move',
|
||||
entry_type = 'file',
|
||||
src_url = 'oil-test:///a.txt',
|
||||
dest_url = 'oil-test:///b.txt',
|
||||
},
|
||||
{
|
||||
type = 'create',
|
||||
entry_type = 'file',
|
||||
url = 'oil-test:///a.txt',
|
||||
},
|
||||
}, actions)
|
||||
end)
|
||||
|
||||
it('resolves MOVE loops', function()
|
||||
local afile = test_adapter.test_set('/a.txt', 'file')
|
||||
local bfile = test_adapter.test_set('/b.txt', 'file')
|
||||
vim.cmd.edit({ args = { 'oil-test:///' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local diffs = {
|
||||
{ type = 'delete', name = 'a.txt', id = afile[FIELD_ID] },
|
||||
{ type = 'new', name = 'b.txt', entry_type = 'file', id = afile[FIELD_ID] },
|
||||
{ type = 'delete', name = 'b.txt', id = bfile[FIELD_ID] },
|
||||
{ type = 'new', name = 'a.txt', entry_type = 'file', id = bfile[FIELD_ID] },
|
||||
}
|
||||
math.randomseed(2983982)
|
||||
local actions = mutator.create_actions_from_diffs({
|
||||
[bufnr] = diffs,
|
||||
})
|
||||
local tmp_url = 'oil-test:///a.txt__oil_tmp_510852'
|
||||
assert.are.same({
|
||||
{
|
||||
type = 'move',
|
||||
entry_type = 'file',
|
||||
src_url = 'oil-test:///a.txt',
|
||||
dest_url = tmp_url,
|
||||
},
|
||||
{
|
||||
type = 'move',
|
||||
entry_type = 'file',
|
||||
src_url = 'oil-test:///b.txt',
|
||||
dest_url = 'oil-test:///a.txt',
|
||||
},
|
||||
{
|
||||
type = 'move',
|
||||
entry_type = 'file',
|
||||
src_url = tmp_url,
|
||||
dest_url = 'oil-test:///b.txt',
|
||||
},
|
||||
}, actions)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('order actions', function()
|
||||
it('Creates files inside dir before move', function()
|
||||
local move = {
|
||||
type = 'move',
|
||||
src_url = 'oil-test:///a',
|
||||
dest_url = 'oil-test:///b',
|
||||
entry_type = 'directory',
|
||||
}
|
||||
local create = { type = 'create', url = 'oil-test:///a/hi.txt', entry_type = 'file' }
|
||||
local actions = { move, create }
|
||||
local ordered_actions = mutator.enforce_action_order(actions)
|
||||
assert.are.same({ create, move }, ordered_actions)
|
||||
end)
|
||||
|
||||
it('Moves file out of parent before deleting parent', function()
|
||||
local move = {
|
||||
type = 'move',
|
||||
src_url = 'oil-test:///a/b.txt',
|
||||
dest_url = 'oil-test:///b.txt',
|
||||
entry_type = 'file',
|
||||
}
|
||||
local delete = { type = 'delete', url = 'oil-test:///a', entry_type = 'directory' }
|
||||
local actions = { delete, move }
|
||||
local ordered_actions = mutator.enforce_action_order(actions)
|
||||
assert.are.same({ move, delete }, ordered_actions)
|
||||
end)
|
||||
|
||||
it('Handles parent child move ordering', function()
|
||||
local move1 = {
|
||||
type = 'move',
|
||||
src_url = 'oil-test:///a/b',
|
||||
dest_url = 'oil-test:///b',
|
||||
entry_type = 'directory',
|
||||
}
|
||||
local move2 = {
|
||||
type = 'move',
|
||||
src_url = 'oil-test:///a',
|
||||
dest_url = 'oil-test:///b/a',
|
||||
entry_type = 'directory',
|
||||
}
|
||||
local actions = { move2, move1 }
|
||||
local ordered_actions = mutator.enforce_action_order(actions)
|
||||
assert.are.same({ move1, move2 }, ordered_actions)
|
||||
end)
|
||||
|
||||
it('Handles a delete inside a moved folder', function()
|
||||
local del = {
|
||||
type = 'delete',
|
||||
url = 'oil-test:///a/b.txt',
|
||||
entry_type = 'file',
|
||||
}
|
||||
local move = {
|
||||
type = 'move',
|
||||
src_url = 'oil-test:///a',
|
||||
dest_url = 'oil-test:///b',
|
||||
entry_type = 'directory',
|
||||
}
|
||||
local actions = { move, del }
|
||||
local ordered_actions = mutator.enforce_action_order(actions)
|
||||
assert.are.same({ del, move }, ordered_actions)
|
||||
end)
|
||||
|
||||
it('Detects move directory loops', function()
|
||||
local move = {
|
||||
type = 'move',
|
||||
src_url = 'oil-test:///a',
|
||||
dest_url = 'oil-test:///a/b',
|
||||
entry_type = 'directory',
|
||||
}
|
||||
assert.has_error(function()
|
||||
mutator.enforce_action_order({ move })
|
||||
end)
|
||||
end)
|
||||
|
||||
it('Detects copy directory loops', function()
|
||||
local move = {
|
||||
type = 'copy',
|
||||
src_url = 'oil-test:///a',
|
||||
dest_url = 'oil-test:///a/b',
|
||||
entry_type = 'directory',
|
||||
}
|
||||
assert.has_error(function()
|
||||
mutator.enforce_action_order({ move })
|
||||
end)
|
||||
end)
|
||||
|
||||
it('Detects nested copy directory loops', function()
|
||||
local move = {
|
||||
type = 'copy',
|
||||
src_url = 'oil-test:///a',
|
||||
dest_url = 'oil-test:///a/b/a',
|
||||
entry_type = 'directory',
|
||||
}
|
||||
assert.has_error(function()
|
||||
mutator.enforce_action_order({ move })
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('change', function()
|
||||
it('applies CHANGE after CREATE', function()
|
||||
local create = { type = 'create', url = 'oil-test:///a/hi.txt', entry_type = 'file' }
|
||||
local change = {
|
||||
type = 'change',
|
||||
url = 'oil-test:///a/hi.txt',
|
||||
entry_type = 'file',
|
||||
column = 'TEST',
|
||||
value = 'TEST',
|
||||
}
|
||||
local actions = { change, create }
|
||||
local ordered_actions = mutator.enforce_action_order(actions)
|
||||
assert.are.same({ create, change }, ordered_actions)
|
||||
end)
|
||||
|
||||
it('applies CHANGE after COPY src', function()
|
||||
local copy = {
|
||||
type = 'copy',
|
||||
src_url = 'oil-test:///a/hi.txt',
|
||||
dest_url = 'oil-test:///b.txt',
|
||||
entry_type = 'file',
|
||||
}
|
||||
local change = {
|
||||
type = 'change',
|
||||
url = 'oil-test:///a/hi.txt',
|
||||
entry_type = 'file',
|
||||
column = 'TEST',
|
||||
value = 'TEST',
|
||||
}
|
||||
local actions = { change, copy }
|
||||
local ordered_actions = mutator.enforce_action_order(actions)
|
||||
assert.are.same({ copy, change }, ordered_actions)
|
||||
end)
|
||||
|
||||
it('applies CHANGE after COPY dest', function()
|
||||
local copy = {
|
||||
type = 'copy',
|
||||
src_url = 'oil-test:///b.txt',
|
||||
dest_url = 'oil-test:///a/hi.txt',
|
||||
entry_type = 'file',
|
||||
}
|
||||
local change = {
|
||||
type = 'change',
|
||||
url = 'oil-test:///a/hi.txt',
|
||||
entry_type = 'file',
|
||||
column = 'TEST',
|
||||
value = 'TEST',
|
||||
}
|
||||
local actions = { change, copy }
|
||||
local ordered_actions = mutator.enforce_action_order(actions)
|
||||
assert.are.same({ copy, change }, ordered_actions)
|
||||
end)
|
||||
|
||||
it('applies CHANGE after MOVE dest', function()
|
||||
local move = {
|
||||
type = 'move',
|
||||
src_url = 'oil-test:///b.txt',
|
||||
dest_url = 'oil-test:///a/hi.txt',
|
||||
entry_type = 'file',
|
||||
}
|
||||
local change = {
|
||||
type = 'change',
|
||||
url = 'oil-test:///a/hi.txt',
|
||||
entry_type = 'file',
|
||||
column = 'TEST',
|
||||
value = 'TEST',
|
||||
}
|
||||
local actions = { change, move }
|
||||
local ordered_actions = mutator.enforce_action_order(actions)
|
||||
assert.are.same({ move, change }, ordered_actions)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('perform actions', function()
|
||||
it('creates new entries', function()
|
||||
local actions = {
|
||||
{ type = 'create', url = 'oil-test:///a.txt', entry_type = 'file' },
|
||||
}
|
||||
test_util.await(mutator.process_actions, 2, actions)
|
||||
local files = cache.list_url('oil-test:///')
|
||||
assert.are.same({
|
||||
['a.txt'] = {
|
||||
[FIELD_ID] = 1,
|
||||
[FIELD_TYPE] = 'file',
|
||||
[FIELD_NAME] = 'a.txt',
|
||||
},
|
||||
}, files)
|
||||
end)
|
||||
|
||||
it('deletes entries', function()
|
||||
local file = test_adapter.test_set('/a.txt', 'file')
|
||||
local actions = {
|
||||
{ type = 'delete', url = 'oil-test:///a.txt', entry_type = 'file' },
|
||||
}
|
||||
test_util.await(mutator.process_actions, 2, actions)
|
||||
local files = cache.list_url('oil-test:///')
|
||||
assert.are.same({}, files)
|
||||
assert.is_nil(cache.get_entry_by_id(file[FIELD_ID]))
|
||||
assert.has_error(function()
|
||||
cache.get_parent_url(file[FIELD_ID])
|
||||
end)
|
||||
end)
|
||||
|
||||
it('moves entries', function()
|
||||
local file = test_adapter.test_set('/a.txt', 'file')
|
||||
local actions = {
|
||||
{
|
||||
type = 'move',
|
||||
src_url = 'oil-test:///a.txt',
|
||||
dest_url = 'oil-test:///b.txt',
|
||||
entry_type = 'file',
|
||||
},
|
||||
}
|
||||
test_util.await(mutator.process_actions, 2, actions)
|
||||
local files = cache.list_url('oil-test:///')
|
||||
local new_entry = {
|
||||
[FIELD_ID] = file[FIELD_ID],
|
||||
[FIELD_TYPE] = 'file',
|
||||
[FIELD_NAME] = 'b.txt',
|
||||
}
|
||||
assert.are.same({
|
||||
['b.txt'] = new_entry,
|
||||
}, files)
|
||||
assert.are.same(new_entry, cache.get_entry_by_id(file[FIELD_ID]))
|
||||
assert.equals('oil-test:///', cache.get_parent_url(file[FIELD_ID]))
|
||||
end)
|
||||
|
||||
it('copies entries', function()
|
||||
local file = test_adapter.test_set('/a.txt', 'file')
|
||||
local actions = {
|
||||
{
|
||||
type = 'copy',
|
||||
src_url = 'oil-test:///a.txt',
|
||||
dest_url = 'oil-test:///b.txt',
|
||||
entry_type = 'file',
|
||||
},
|
||||
}
|
||||
test_util.await(mutator.process_actions, 2, actions)
|
||||
local files = cache.list_url('oil-test:///')
|
||||
local new_entry = {
|
||||
[FIELD_ID] = file[FIELD_ID] + 1,
|
||||
[FIELD_TYPE] = 'file',
|
||||
[FIELD_NAME] = 'b.txt',
|
||||
}
|
||||
assert.are.same({
|
||||
['a.txt'] = file,
|
||||
['b.txt'] = new_entry,
|
||||
}, files)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
249
spec/parser_spec.lua
Normal file
249
spec/parser_spec.lua
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
local constants = require('oil.constants')
|
||||
local parser = require('oil.mutator.parser')
|
||||
local test_adapter = require('oil.adapters.test')
|
||||
local test_util = require('spec.test_util')
|
||||
local util = require('oil.util')
|
||||
local view = require('oil.view')
|
||||
|
||||
local FIELD_ID = constants.FIELD_ID
|
||||
local FIELD_META = constants.FIELD_META
|
||||
|
||||
local function set_lines(bufnr, lines)
|
||||
vim.bo[bufnr].modifiable = true
|
||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, lines)
|
||||
end
|
||||
|
||||
describe('parser', function()
|
||||
after_each(function()
|
||||
test_util.reset_editor()
|
||||
end)
|
||||
|
||||
it('detects new files', function()
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
set_lines(bufnr, {
|
||||
'a.txt',
|
||||
})
|
||||
local diffs = parser.parse(bufnr)
|
||||
assert.are.same({ { entry_type = 'file', name = 'a.txt', type = 'new' } }, diffs)
|
||||
end)
|
||||
|
||||
it('detects new directories', function()
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
set_lines(bufnr, {
|
||||
'foo/',
|
||||
})
|
||||
local diffs = parser.parse(bufnr)
|
||||
assert.are.same({ { entry_type = 'directory', name = 'foo', type = 'new' } }, diffs)
|
||||
end)
|
||||
|
||||
it('detects new links', function()
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
set_lines(bufnr, {
|
||||
'a.txt -> b.txt',
|
||||
})
|
||||
local diffs = parser.parse(bufnr)
|
||||
assert.are.same(
|
||||
{ { entry_type = 'link', name = 'a.txt', type = 'new', link = 'b.txt' } },
|
||||
diffs
|
||||
)
|
||||
end)
|
||||
|
||||
it('detects deleted files', function()
|
||||
local file = test_adapter.test_set('/foo/a.txt', 'file')
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
set_lines(bufnr, {})
|
||||
local diffs = parser.parse(bufnr)
|
||||
assert.are.same({
|
||||
{ name = 'a.txt', type = 'delete', id = file[FIELD_ID] },
|
||||
}, diffs)
|
||||
end)
|
||||
|
||||
it('detects deleted directories', function()
|
||||
local dir = test_adapter.test_set('/foo/bar', 'directory')
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
set_lines(bufnr, {})
|
||||
local diffs = parser.parse(bufnr)
|
||||
assert.are.same({
|
||||
{ name = 'bar', type = 'delete', id = dir[FIELD_ID] },
|
||||
}, diffs)
|
||||
end)
|
||||
|
||||
it('detects deleted links', function()
|
||||
local file = test_adapter.test_set('/foo/a.txt', 'link')
|
||||
file[FIELD_META] = { link = 'b.txt' }
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
set_lines(bufnr, {})
|
||||
local diffs = parser.parse(bufnr)
|
||||
assert.are.same({
|
||||
{ name = 'a.txt', type = 'delete', id = file[FIELD_ID] },
|
||||
}, diffs)
|
||||
end)
|
||||
|
||||
it('ignores empty lines', function()
|
||||
local file = test_adapter.test_set('/foo/a.txt', 'file')
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local cols = view.format_entry_cols(file, {}, {}, test_adapter, false)
|
||||
local lines = util.render_table({ cols }, {})
|
||||
table.insert(lines, '')
|
||||
table.insert(lines, ' ')
|
||||
set_lines(bufnr, lines)
|
||||
local diffs = parser.parse(bufnr)
|
||||
assert.are.same({}, diffs)
|
||||
end)
|
||||
|
||||
it('errors on missing filename', function()
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
set_lines(bufnr, {
|
||||
'/008',
|
||||
})
|
||||
local _, errors = parser.parse(bufnr)
|
||||
assert.are_same({
|
||||
{
|
||||
message = 'Malformed ID at start of line',
|
||||
lnum = 0,
|
||||
end_lnum = 1,
|
||||
col = 0,
|
||||
},
|
||||
}, errors)
|
||||
end)
|
||||
|
||||
it('errors on empty dirname', function()
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
set_lines(bufnr, {
|
||||
'/008 /',
|
||||
})
|
||||
local _, errors = parser.parse(bufnr)
|
||||
assert.are.same({
|
||||
{
|
||||
message = 'No filename found',
|
||||
lnum = 0,
|
||||
end_lnum = 1,
|
||||
col = 0,
|
||||
},
|
||||
}, errors)
|
||||
end)
|
||||
|
||||
it('errors on duplicate names', function()
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
set_lines(bufnr, {
|
||||
'foo',
|
||||
'foo/',
|
||||
})
|
||||
local _, errors = parser.parse(bufnr)
|
||||
assert.are.same({
|
||||
{
|
||||
message = 'Duplicate filename',
|
||||
lnum = 1,
|
||||
end_lnum = 2,
|
||||
col = 0,
|
||||
},
|
||||
}, errors)
|
||||
end)
|
||||
|
||||
it('errors on duplicate names for existing files', function()
|
||||
local file = test_adapter.test_set('/foo/a.txt', 'file')
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
set_lines(bufnr, {
|
||||
'a.txt',
|
||||
string.format('/%d a.txt', file[FIELD_ID]),
|
||||
})
|
||||
local _, errors = parser.parse(bufnr)
|
||||
assert.are.same({
|
||||
{
|
||||
message = 'Duplicate filename',
|
||||
lnum = 1,
|
||||
end_lnum = 2,
|
||||
col = 0,
|
||||
},
|
||||
}, errors)
|
||||
end)
|
||||
|
||||
it('ignores new dirs with empty name', function()
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
set_lines(bufnr, {
|
||||
'/',
|
||||
})
|
||||
local diffs = parser.parse(bufnr)
|
||||
assert.are.same({}, diffs)
|
||||
end)
|
||||
|
||||
it('parses a rename as a delete + new', function()
|
||||
local file = test_adapter.test_set('/foo/a.txt', 'file')
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
set_lines(bufnr, {
|
||||
string.format('/%d b.txt', file[FIELD_ID]),
|
||||
})
|
||||
local diffs = parser.parse(bufnr)
|
||||
assert.are.same({
|
||||
{ type = 'new', id = file[FIELD_ID], name = 'b.txt', entry_type = 'file' },
|
||||
{ type = 'delete', id = file[FIELD_ID], name = 'a.txt' },
|
||||
}, diffs)
|
||||
end)
|
||||
|
||||
it('detects a new trailing slash as a delete + create', function()
|
||||
local file = test_adapter.test_set('/foo', 'file')
|
||||
vim.cmd.edit({ args = { 'oil-test:///' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
set_lines(bufnr, {
|
||||
string.format('/%d foo/', file[FIELD_ID]),
|
||||
})
|
||||
local diffs = parser.parse(bufnr)
|
||||
assert.are.same({
|
||||
{ type = 'new', name = 'foo', entry_type = 'directory' },
|
||||
{ type = 'delete', id = file[FIELD_ID], name = 'foo' },
|
||||
}, diffs)
|
||||
end)
|
||||
|
||||
it('detects renamed files that conflict', function()
|
||||
local afile = test_adapter.test_set('/foo/a.txt', 'file')
|
||||
local bfile = test_adapter.test_set('/foo/b.txt', 'file')
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
set_lines(bufnr, {
|
||||
string.format('/%d a.txt', bfile[FIELD_ID]),
|
||||
string.format('/%d b.txt', afile[FIELD_ID]),
|
||||
})
|
||||
local diffs = parser.parse(bufnr)
|
||||
local first_two = { diffs[1], diffs[2] }
|
||||
local last_two = { diffs[3], diffs[4] }
|
||||
table.sort(first_two, function(a, b)
|
||||
return a.id < b.id
|
||||
end)
|
||||
table.sort(last_two, function(a, b)
|
||||
return a.id < b.id
|
||||
end)
|
||||
assert.are.same({
|
||||
{ name = 'b.txt', type = 'new', id = afile[FIELD_ID], entry_type = 'file' },
|
||||
{ name = 'a.txt', type = 'new', id = bfile[FIELD_ID], entry_type = 'file' },
|
||||
}, first_two)
|
||||
assert.are.same({
|
||||
{ name = 'a.txt', type = 'delete', id = afile[FIELD_ID] },
|
||||
{ name = 'b.txt', type = 'delete', id = bfile[FIELD_ID] },
|
||||
}, last_two)
|
||||
end)
|
||||
|
||||
it('views link targets with trailing slashes as the same', function()
|
||||
local file = test_adapter.test_set('/foo/mydir', 'link')
|
||||
file[FIELD_META] = { link = 'dir/' }
|
||||
vim.cmd.edit({ args = { 'oil-test:///foo/' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
set_lines(bufnr, {
|
||||
string.format('/%d mydir/ -> dir/', file[FIELD_ID]),
|
||||
})
|
||||
local diffs = parser.parse(bufnr)
|
||||
assert.are.same({}, diffs)
|
||||
end)
|
||||
end)
|
||||
32
spec/path_spec.lua
Normal file
32
spec/path_spec.lua
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
local pathutil = require('oil.pathutil')
|
||||
describe('pathutil', function()
|
||||
it('calculates parent path', function()
|
||||
local cases = {
|
||||
{ '/foo/bar', '/foo/' },
|
||||
{ '/foo/bar/', '/foo/' },
|
||||
{ '/', '/' },
|
||||
{ '', '' },
|
||||
{ 'foo/bar/', 'foo/' },
|
||||
{ 'foo', '' },
|
||||
}
|
||||
for _, case in ipairs(cases) do
|
||||
local input, expected = unpack(case)
|
||||
local output = pathutil.parent(input)
|
||||
assert.equals(expected, output, string.format('Parent path "%s" failed', input))
|
||||
end
|
||||
end)
|
||||
|
||||
it('calculates basename', function()
|
||||
local cases = {
|
||||
{ '/foo/bar', 'bar' },
|
||||
{ '/foo/bar/', 'bar' },
|
||||
{ '/', nil },
|
||||
{ '', nil },
|
||||
}
|
||||
for _, case in ipairs(cases) do
|
||||
local input, expected = unpack(case)
|
||||
local output = pathutil.basename(input)
|
||||
assert.equals(expected, output, string.format('Basename "%s" failed', input))
|
||||
end
|
||||
end)
|
||||
end)
|
||||
40
spec/preview_spec.lua
Normal file
40
spec/preview_spec.lua
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
local TmpDir = require('spec.tmpdir')
|
||||
local oil = require('oil')
|
||||
local test_util = require('spec.test_util')
|
||||
local util = require('oil.util')
|
||||
|
||||
describe('oil preview', function()
|
||||
local tmpdir
|
||||
before_each(function()
|
||||
tmpdir = TmpDir.new()
|
||||
end)
|
||||
after_each(function()
|
||||
if tmpdir then
|
||||
tmpdir:dispose()
|
||||
end
|
||||
test_util.reset_editor()
|
||||
end)
|
||||
|
||||
it('opens preview window', function()
|
||||
tmpdir:create({ 'a.txt' })
|
||||
test_util.oil_open(tmpdir.path)
|
||||
test_util.await(oil.open_preview, 2)
|
||||
local preview_win = util.get_preview_win()
|
||||
assert.not_nil(preview_win)
|
||||
assert(preview_win)
|
||||
local bufnr = vim.api.nvim_win_get_buf(preview_win)
|
||||
local preview_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||
assert.are.same({ 'a.txt' }, preview_lines)
|
||||
end)
|
||||
|
||||
it('opens preview window when open(preview={})', function()
|
||||
tmpdir:create({ 'a.txt' })
|
||||
test_util.oil_open(tmpdir.path, { preview = {} })
|
||||
local preview_win = util.get_preview_win()
|
||||
assert.not_nil(preview_win)
|
||||
assert(preview_win)
|
||||
local bufnr = vim.api.nvim_win_get_buf(preview_win)
|
||||
local preview_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||
assert.are.same({ 'a.txt' }, preview_lines)
|
||||
end)
|
||||
end)
|
||||
137
spec/regression_spec.lua
Normal file
137
spec/regression_spec.lua
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
local TmpDir = require('spec.tmpdir')
|
||||
local actions = require('oil.actions')
|
||||
local oil = require('oil')
|
||||
local test_util = require('spec.test_util')
|
||||
local view = require('oil.view')
|
||||
|
||||
describe('regression tests', function()
|
||||
local tmpdir
|
||||
before_each(function()
|
||||
tmpdir = TmpDir.new()
|
||||
end)
|
||||
after_each(function()
|
||||
if tmpdir then
|
||||
tmpdir:dispose()
|
||||
tmpdir = nil
|
||||
end
|
||||
test_util.reset_editor()
|
||||
end)
|
||||
|
||||
it('can edit dirs that will be renamed to an existing buffer', function()
|
||||
vim.cmd.edit({ args = { 'README.md' } })
|
||||
vim.cmd.vsplit()
|
||||
vim.cmd.edit({ args = { '%:p:h' } })
|
||||
assert.equals('oil', vim.bo.filetype)
|
||||
vim.cmd.wincmd({ args = { 'p' } })
|
||||
assert.equals('markdown', vim.bo.filetype)
|
||||
vim.cmd.edit({ args = { '%:p:h' } })
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
assert.equals('oil', vim.bo.filetype)
|
||||
end)
|
||||
|
||||
it('places the cursor on correct entry when opening on file', function()
|
||||
vim.cmd.edit({ args = { '.' } })
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
local entry = oil.get_cursor_entry()
|
||||
assert.not_nil(entry)
|
||||
assert.not_equals('README.md', entry and entry.name)
|
||||
vim.cmd.edit({ args = { 'README.md' } })
|
||||
view.delete_hidden_buffers()
|
||||
oil.open()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
entry = oil.get_cursor_entry()
|
||||
assert.equals('README.md', entry and entry.name)
|
||||
end)
|
||||
|
||||
it("doesn't close floating windows oil didn't open itself", function()
|
||||
local winid = vim.api.nvim_open_win(vim.fn.bufadd('README.md'), true, {
|
||||
relative = 'editor',
|
||||
row = 1,
|
||||
col = 1,
|
||||
width = 100,
|
||||
height = 100,
|
||||
})
|
||||
oil.open()
|
||||
vim.wait(10)
|
||||
oil.close()
|
||||
vim.wait(10)
|
||||
assert.equals(winid, vim.api.nvim_get_current_win())
|
||||
end)
|
||||
|
||||
it("doesn't close splits on oil.close", function()
|
||||
vim.cmd.edit({ args = { 'README.md' } })
|
||||
vim.cmd.vsplit()
|
||||
local winid = vim.api.nvim_get_current_win()
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
oil.open()
|
||||
vim.wait(10)
|
||||
oil.close()
|
||||
vim.wait(10)
|
||||
assert.equals(2, #vim.api.nvim_tabpage_list_wins(0))
|
||||
assert.equals(winid, vim.api.nvim_get_current_win())
|
||||
assert.equals(bufnr, vim.api.nvim_get_current_buf())
|
||||
end)
|
||||
|
||||
it('Returns to empty buffer on close', function()
|
||||
oil.open()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
oil.close()
|
||||
assert.not_equals('oil', vim.bo.filetype)
|
||||
assert.equals('', vim.api.nvim_buf_get_name(0))
|
||||
end)
|
||||
|
||||
it('All buffers set nomodified after save', function()
|
||||
tmpdir:create({ 'a.txt' })
|
||||
vim.cmd.edit({ args = { 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') } })
|
||||
local first_dir = vim.api.nvim_get_current_buf()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
test_util.feedkeys({ 'dd', 'itest/<esc>', '<CR>' }, 10)
|
||||
vim.wait(1000, function()
|
||||
return vim.bo.modifiable
|
||||
end, 10)
|
||||
test_util.feedkeys({ 'p' }, 10)
|
||||
oil.save({ confirm = false })
|
||||
vim.wait(1000, function()
|
||||
return vim.bo.modifiable
|
||||
end, 10)
|
||||
tmpdir:assert_fs({
|
||||
['test/a.txt'] = 'a.txt',
|
||||
})
|
||||
assert.falsy(vim.bo[first_dir].modified)
|
||||
end)
|
||||
|
||||
it("refreshing buffer doesn't lose track of it", function()
|
||||
vim.cmd.edit({ args = { '.' } })
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
vim.cmd.edit({ bang = true })
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
assert.are.same({ bufnr }, require('oil.view').get_all_buffers())
|
||||
end)
|
||||
|
||||
it('can copy a file multiple times', function()
|
||||
test_util.actions.open({ tmpdir.path })
|
||||
vim.api.nvim_feedkeys('ifoo.txt', 'x', true)
|
||||
test_util.actions.save()
|
||||
vim.api.nvim_feedkeys('yyp$ciWbar.txt', 'x', true)
|
||||
vim.api.nvim_feedkeys('yyp$ciWbaz.txt', 'x', true)
|
||||
test_util.actions.save()
|
||||
assert.are.same({ 'bar.txt', 'baz.txt', 'foo.txt' }, test_util.parse_entries(0))
|
||||
tmpdir:assert_fs({
|
||||
['foo.txt'] = '',
|
||||
['bar.txt'] = '',
|
||||
['baz.txt'] = '',
|
||||
})
|
||||
end)
|
||||
|
||||
it('can open files from floating window', function()
|
||||
tmpdir:create({ 'a.txt' })
|
||||
oil.open_float(tmpdir.path)
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
actions.select.callback()
|
||||
vim.wait(1000, function()
|
||||
return vim.fn.expand('%:t') == 'a.txt'
|
||||
end, 10)
|
||||
assert.equals('a.txt', vim.fn.expand('%:t'))
|
||||
end)
|
||||
end)
|
||||
89
spec/select_spec.lua
Normal file
89
spec/select_spec.lua
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
local oil = require('oil')
|
||||
local test_util = require('spec.test_util')
|
||||
|
||||
describe('oil select', function()
|
||||
after_each(function()
|
||||
test_util.reset_editor()
|
||||
end)
|
||||
|
||||
it('opens file under cursor', function()
|
||||
test_util.oil_open()
|
||||
vim.cmd.normal({ args = { 'G' } })
|
||||
test_util.await(oil.select, 2)
|
||||
assert.equals(1, #vim.api.nvim_tabpage_list_wins(0))
|
||||
assert.not_equals('oil', vim.bo.filetype)
|
||||
end)
|
||||
|
||||
it('opens file in new tab', function()
|
||||
test_util.oil_open()
|
||||
local tabpage = vim.api.nvim_get_current_tabpage()
|
||||
test_util.await(oil.select, 2, { tab = true })
|
||||
assert.equals(2, #vim.api.nvim_list_tabpages())
|
||||
assert.equals(1, #vim.api.nvim_tabpage_list_wins(0))
|
||||
assert.not_equals(tabpage, vim.api.nvim_get_current_tabpage())
|
||||
end)
|
||||
|
||||
it('opens file in new split', function()
|
||||
test_util.oil_open()
|
||||
local winid = vim.api.nvim_get_current_win()
|
||||
test_util.await(oil.select, 2, { vertical = true })
|
||||
assert.equals(1, #vim.api.nvim_list_tabpages())
|
||||
assert.equals(2, #vim.api.nvim_tabpage_list_wins(0))
|
||||
assert.not_equals(winid, vim.api.nvim_get_current_win())
|
||||
end)
|
||||
|
||||
it('opens multiple files in new tabs', function()
|
||||
test_util.oil_open()
|
||||
vim.api.nvim_feedkeys('Vj', 'x', true)
|
||||
local tabpage = vim.api.nvim_get_current_tabpage()
|
||||
test_util.await(oil.select, 2, { tab = true })
|
||||
assert.equals(3, #vim.api.nvim_list_tabpages())
|
||||
assert.equals(1, #vim.api.nvim_tabpage_list_wins(0))
|
||||
assert.not_equals(tabpage, vim.api.nvim_get_current_tabpage())
|
||||
end)
|
||||
|
||||
it('opens multiple files in new splits', function()
|
||||
test_util.oil_open()
|
||||
vim.api.nvim_feedkeys('Vj', 'x', true)
|
||||
local winid = vim.api.nvim_get_current_win()
|
||||
test_util.await(oil.select, 2, { vertical = true })
|
||||
assert.equals(1, #vim.api.nvim_list_tabpages())
|
||||
assert.equals(3, #vim.api.nvim_tabpage_list_wins(0))
|
||||
assert.not_equals(winid, vim.api.nvim_get_current_win())
|
||||
end)
|
||||
|
||||
describe('close after open', function()
|
||||
it('same window', function()
|
||||
vim.cmd.edit({ args = { 'foo' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
test_util.oil_open()
|
||||
vim.cmd.normal({ args = { 'G' } })
|
||||
test_util.await(oil.select, 2, { close = true })
|
||||
assert.equals(1, #vim.api.nvim_tabpage_list_wins(0))
|
||||
assert.not_equals(bufnr, vim.api.nvim_get_current_buf())
|
||||
assert.not_equals('oil', vim.bo.filetype)
|
||||
end)
|
||||
|
||||
it('split', function()
|
||||
vim.cmd.edit({ args = { 'foo' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local winid = vim.api.nvim_get_current_win()
|
||||
test_util.oil_open()
|
||||
test_util.await(oil.select, 2, { vertical = true, close = true })
|
||||
assert.equals(2, #vim.api.nvim_tabpage_list_wins(0))
|
||||
assert.equals(bufnr, vim.api.nvim_win_get_buf(winid))
|
||||
end)
|
||||
|
||||
it('tab', function()
|
||||
vim.cmd.edit({ args = { 'foo' } })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local tabpage = vim.api.nvim_get_current_tabpage()
|
||||
test_util.oil_open()
|
||||
test_util.await(oil.select, 2, { tab = true, close = true })
|
||||
assert.equals(1, #vim.api.nvim_tabpage_list_wins(0))
|
||||
assert.equals(2, #vim.api.nvim_list_tabpages())
|
||||
vim.api.nvim_set_current_tabpage(tabpage)
|
||||
assert.equals(bufnr, vim.api.nvim_get_current_buf())
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
176
spec/test_util.lua
Normal file
176
spec/test_util.lua
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
local cache = require('oil.cache')
|
||||
local test_adapter = require('oil.adapters.test')
|
||||
local util = require('oil.util')
|
||||
local M = {}
|
||||
|
||||
M.reset_editor = function()
|
||||
require('oil').setup({
|
||||
columms = {},
|
||||
adapters = {
|
||||
['oil-test://'] = 'test',
|
||||
},
|
||||
prompt_save_on_select_new_entry = false,
|
||||
})
|
||||
vim.cmd.tabonly({ mods = { silent = true } })
|
||||
for i, winid in ipairs(vim.api.nvim_tabpage_list_wins(0)) do
|
||||
if i > 1 then
|
||||
vim.api.nvim_win_close(winid, true)
|
||||
end
|
||||
end
|
||||
vim.api.nvim_win_set_buf(0, vim.api.nvim_create_buf(false, true))
|
||||
for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
|
||||
vim.api.nvim_buf_delete(bufnr, { force = true })
|
||||
end
|
||||
cache.clear_everything()
|
||||
test_adapter.test_clear()
|
||||
end
|
||||
|
||||
local function throwiferr(err, ...)
|
||||
if err then
|
||||
error(err)
|
||||
else
|
||||
return ...
|
||||
end
|
||||
end
|
||||
|
||||
M.await = function(fn, nargs, ...)
|
||||
local done = false
|
||||
local results
|
||||
local n_results = 0
|
||||
local args = { ... }
|
||||
args[nargs] = function(...)
|
||||
results = { ... }
|
||||
n_results = select('#', ...)
|
||||
done = true
|
||||
end
|
||||
fn(unpack(args, 1, nargs))
|
||||
vim.wait(10000, function()
|
||||
return done
|
||||
end, 10)
|
||||
if not done then
|
||||
error('M.await timed out')
|
||||
end
|
||||
return unpack(results, 1, n_results)
|
||||
end
|
||||
|
||||
M.await_throwiferr = function(fn, nargs, ...)
|
||||
return throwiferr(M.await(fn, nargs, ...))
|
||||
end
|
||||
|
||||
M.oil_open = function(...)
|
||||
M.await(require('oil').open, 3, ...)
|
||||
end
|
||||
|
||||
M.wait_for_autocmd = function(autocmd)
|
||||
local triggered = false
|
||||
local opts = {
|
||||
pattern = '*',
|
||||
nested = true,
|
||||
once = true,
|
||||
}
|
||||
if type(autocmd) == 'table' then
|
||||
opts = vim.tbl_extend('force', opts, autocmd)
|
||||
autocmd = autocmd[1]
|
||||
opts[1] = nil
|
||||
end
|
||||
opts.callback = vim.schedule_wrap(function()
|
||||
triggered = true
|
||||
end)
|
||||
vim.api.nvim_create_autocmd(autocmd, opts)
|
||||
vim.wait(10000, function()
|
||||
return triggered
|
||||
end, 10)
|
||||
if not triggered then
|
||||
error('wait_for_autocmd timed out waiting for ' .. tostring(autocmd))
|
||||
end
|
||||
end
|
||||
|
||||
M.wait_oil_ready = function()
|
||||
local ready = false
|
||||
util.run_after_load(
|
||||
0,
|
||||
vim.schedule_wrap(function()
|
||||
ready = true
|
||||
end)
|
||||
)
|
||||
vim.wait(10000, function()
|
||||
return ready
|
||||
end, 10)
|
||||
if not ready then
|
||||
error('wait_oil_ready timed out')
|
||||
end
|
||||
end
|
||||
|
||||
---@param actions string[]
|
||||
---@param timestep integer
|
||||
M.feedkeys = function(actions, timestep)
|
||||
timestep = timestep or 10
|
||||
vim.wait(timestep)
|
||||
for _, action in ipairs(actions) do
|
||||
vim.wait(timestep)
|
||||
local escaped = vim.api.nvim_replace_termcodes(action, true, false, true)
|
||||
vim.api.nvim_feedkeys(escaped, 'm', true)
|
||||
end
|
||||
vim.wait(timestep)
|
||||
vim.api.nvim_feedkeys('', 'x', true)
|
||||
vim.wait(timestep)
|
||||
end
|
||||
|
||||
M.actions = {
|
||||
---Open oil and wait for it to finish rendering
|
||||
---@param args string[]
|
||||
open = function(args)
|
||||
vim.schedule(function()
|
||||
vim.cmd.Oil({ args = args })
|
||||
if vim.b.oil_ready then
|
||||
vim.api.nvim_exec_autocmds('User', {
|
||||
pattern = 'OilEnter',
|
||||
modeline = false,
|
||||
data = { buf = vim.api.nvim_get_current_buf() },
|
||||
})
|
||||
end
|
||||
end)
|
||||
M.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
end,
|
||||
|
||||
---Save all changes and wait for operation to complete
|
||||
save = function()
|
||||
vim.schedule_wrap(require('oil').save)({ confirm = false })
|
||||
M.wait_for_autocmd({ 'User', pattern = 'OilMutationComplete' })
|
||||
end,
|
||||
|
||||
---@param bufnr? integer
|
||||
reload = function(bufnr)
|
||||
M.await(require('oil.view').render_buffer_async, 3, bufnr or 0)
|
||||
end,
|
||||
|
||||
---Move cursor to a file or directory in an oil buffer
|
||||
---@param filename string
|
||||
focus = function(filename)
|
||||
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, true)
|
||||
local search = ' ' .. filename .. '$'
|
||||
for i, line in ipairs(lines) do
|
||||
if line:match(search) then
|
||||
vim.api.nvim_win_set_cursor(0, { i, 0 })
|
||||
return
|
||||
end
|
||||
end
|
||||
error('Could not find file ' .. filename)
|
||||
end,
|
||||
}
|
||||
|
||||
---Get the raw list of filenames from an unmodified oil buffer
|
||||
---@param bufnr? integer
|
||||
---@return string[]
|
||||
M.parse_entries = function(bufnr)
|
||||
bufnr = bufnr or 0
|
||||
if vim.bo[bufnr].modified then
|
||||
error("parse_entries doesn't work on a modified oil buffer")
|
||||
end
|
||||
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, true)
|
||||
return vim.tbl_map(function(line)
|
||||
return line:match('^/%d+ +(.+)$')
|
||||
end, lines)
|
||||
end
|
||||
|
||||
return M
|
||||
156
spec/tmpdir.lua
Normal file
156
spec/tmpdir.lua
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
local fs = require('oil.fs')
|
||||
local test_util = require('spec.test_util')
|
||||
|
||||
---@param path string
|
||||
local function touch(path)
|
||||
local fd, open_err = vim.loop.fs_open(path, 'w', 420) -- 0644
|
||||
if not fd then
|
||||
error(open_err)
|
||||
end
|
||||
local shortpath = path:gsub('^[^' .. fs.sep .. ']*' .. fs.sep, '')
|
||||
local _, write_err = vim.loop.fs_write(fd, shortpath)
|
||||
if write_err then
|
||||
error(write_err)
|
||||
end
|
||||
vim.loop.fs_close(fd)
|
||||
end
|
||||
|
||||
---@param filepath string
|
||||
---@return boolean
|
||||
local function exists(filepath)
|
||||
local stat = vim.loop.fs_stat(filepath)
|
||||
return stat ~= nil and stat.type ~= nil
|
||||
end
|
||||
|
||||
local TmpDir = {}
|
||||
|
||||
TmpDir.new = function()
|
||||
local path, err = vim.loop.fs_mkdtemp('oil_test_XXXXXXXXX')
|
||||
if not path then
|
||||
error(err)
|
||||
end
|
||||
return setmetatable({ path = path }, {
|
||||
__index = TmpDir,
|
||||
})
|
||||
end
|
||||
|
||||
---@param paths string[]
|
||||
function TmpDir:create(paths)
|
||||
for _, path in ipairs(paths) do
|
||||
local pieces = vim.split(path, fs.sep)
|
||||
local partial_path = self.path
|
||||
for i, piece in ipairs(pieces) do
|
||||
partial_path = fs.join(partial_path, piece)
|
||||
if i == #pieces and not vim.endswith(partial_path, fs.sep) then
|
||||
touch(partial_path)
|
||||
elseif not exists(partial_path) then
|
||||
vim.loop.fs_mkdir(partial_path, 493)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@param filepath string
|
||||
---@return string?
|
||||
local read_file = function(filepath)
|
||||
local fd = vim.loop.fs_open(filepath, 'r', 420)
|
||||
if not fd then
|
||||
return nil
|
||||
end
|
||||
local stat = vim.loop.fs_fstat(fd)
|
||||
local content = vim.loop.fs_read(fd, stat.size)
|
||||
vim.loop.fs_close(fd)
|
||||
return content
|
||||
end
|
||||
|
||||
---@param dir string
|
||||
local function walk(dir)
|
||||
local ret = {}
|
||||
for name, type in vim.fs.dir(dir) do
|
||||
table.insert(ret, {
|
||||
name = name,
|
||||
type = type,
|
||||
root = dir,
|
||||
})
|
||||
if type == 'directory' then
|
||||
vim.list_extend(ret, walk(fs.join(dir, name)))
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
---@param paths table<string, string>
|
||||
local assert_fs = function(root, paths)
|
||||
local unlisted_dirs = {}
|
||||
for k in pairs(paths) do
|
||||
local pieces = vim.split(k, '/')
|
||||
local partial_path = ''
|
||||
for i, piece in ipairs(pieces) do
|
||||
partial_path = partial_path .. piece .. '/'
|
||||
if i ~= #pieces then
|
||||
unlisted_dirs[partial_path] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
for k in pairs(unlisted_dirs) do
|
||||
paths[k] = true
|
||||
end
|
||||
|
||||
local entries = walk(root)
|
||||
for _, entry in ipairs(entries) do
|
||||
local fullpath = fs.join(entry.root, entry.name)
|
||||
local shortpath = fullpath:sub(root:len() + 2)
|
||||
if entry.type == 'directory' then
|
||||
shortpath = shortpath .. '/'
|
||||
end
|
||||
local expected_content = paths[shortpath]
|
||||
paths[shortpath] = nil
|
||||
assert(expected_content, string.format("Unexpected entry '%s'", shortpath))
|
||||
if entry.type == 'file' then
|
||||
local data = read_file(fullpath)
|
||||
assert(
|
||||
expected_content == data,
|
||||
string.format(
|
||||
"File '%s' expected content '%s' received '%s'",
|
||||
shortpath,
|
||||
expected_content,
|
||||
data
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
for k, v in pairs(paths) do
|
||||
assert(
|
||||
not k,
|
||||
string.format(
|
||||
"Expected %s '%s', but it was not found",
|
||||
v == true and 'directory' or 'file',
|
||||
k
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
---@param paths table<string, string>
|
||||
function TmpDir:assert_fs(paths)
|
||||
assert_fs(self.path, paths)
|
||||
end
|
||||
|
||||
function TmpDir:assert_exists(path)
|
||||
path = fs.join(self.path, path)
|
||||
local stat = vim.loop.fs_stat(path)
|
||||
assert(stat, string.format("Expected path '%s' to exist", path))
|
||||
end
|
||||
|
||||
function TmpDir:assert_not_exists(path)
|
||||
path = fs.join(self.path, path)
|
||||
local stat = vim.loop.fs_stat(path)
|
||||
assert(not stat, string.format("Expected path '%s' to not exist", path))
|
||||
end
|
||||
|
||||
function TmpDir:dispose()
|
||||
test_util.await_throwiferr(fs.recursive_delete, 3, 'directory', self.path)
|
||||
end
|
||||
|
||||
return TmpDir
|
||||
149
spec/trash_spec.lua
Normal file
149
spec/trash_spec.lua
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
local TmpDir = require('spec.tmpdir')
|
||||
local test_util = require('spec.test_util')
|
||||
|
||||
describe('freedesktop', function()
|
||||
local tmpdir
|
||||
local tmphome
|
||||
local home = vim.env.XDG_DATA_HOME
|
||||
before_each(function()
|
||||
require('oil.config').delete_to_trash = true
|
||||
tmpdir = TmpDir.new()
|
||||
tmphome = TmpDir.new()
|
||||
package.loaded['oil.adapters.trash'] = require('oil.adapters.trash.freedesktop')
|
||||
vim.env.XDG_DATA_HOME = tmphome.path
|
||||
end)
|
||||
after_each(function()
|
||||
vim.env.XDG_DATA_HOME = home
|
||||
if tmpdir then
|
||||
tmpdir:dispose()
|
||||
end
|
||||
if tmphome then
|
||||
tmphome:dispose()
|
||||
end
|
||||
test_util.reset_editor()
|
||||
package.loaded['oil.adapters.trash'] = nil
|
||||
end)
|
||||
|
||||
it('files can be moved to the trash', function()
|
||||
tmpdir:create({ 'a.txt', 'foo/b.txt' })
|
||||
test_util.actions.open({ tmpdir.path })
|
||||
test_util.actions.focus('a.txt')
|
||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
||||
test_util.actions.open({ '--trash', tmpdir.path })
|
||||
vim.api.nvim_feedkeys('p', 'x', true)
|
||||
test_util.actions.save()
|
||||
tmpdir:assert_not_exists('a.txt')
|
||||
tmpdir:assert_exists('foo/b.txt')
|
||||
test_util.actions.reload()
|
||||
assert.are.same({ 'a.txt' }, test_util.parse_entries(0))
|
||||
end)
|
||||
|
||||
it('deleting a file moves it to trash', function()
|
||||
tmpdir:create({ 'a.txt', 'foo/b.txt' })
|
||||
test_util.actions.open({ tmpdir.path })
|
||||
test_util.actions.focus('a.txt')
|
||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
||||
test_util.actions.save()
|
||||
tmpdir:assert_not_exists('a.txt')
|
||||
tmpdir:assert_exists('foo/b.txt')
|
||||
test_util.actions.open({ '--trash', tmpdir.path })
|
||||
assert.are.same({ 'a.txt' }, test_util.parse_entries(0))
|
||||
end)
|
||||
|
||||
it('deleting a directory moves it to trash', function()
|
||||
tmpdir:create({ 'a.txt', 'foo/b.txt' })
|
||||
test_util.actions.open({ tmpdir.path })
|
||||
test_util.actions.focus('foo/')
|
||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
||||
test_util.actions.save()
|
||||
tmpdir:assert_not_exists('foo')
|
||||
tmpdir:assert_exists('a.txt')
|
||||
test_util.actions.open({ '--trash', tmpdir.path })
|
||||
assert.are.same({ 'foo/' }, test_util.parse_entries(0))
|
||||
end)
|
||||
|
||||
it('deleting a file from trash deletes it permanently', function()
|
||||
tmpdir:create({ 'a.txt' })
|
||||
test_util.actions.open({ tmpdir.path })
|
||||
test_util.actions.focus('a.txt')
|
||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
||||
test_util.actions.save()
|
||||
test_util.actions.open({ '--trash', tmpdir.path })
|
||||
test_util.actions.focus('a.txt')
|
||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
||||
test_util.actions.save()
|
||||
test_util.actions.reload()
|
||||
tmpdir:assert_not_exists('a.txt')
|
||||
assert.are.same({}, test_util.parse_entries(0))
|
||||
end)
|
||||
|
||||
it('cannot create files in the trash', function()
|
||||
tmpdir:create({ 'a.txt' })
|
||||
test_util.actions.open({ tmpdir.path })
|
||||
test_util.actions.focus('a.txt')
|
||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
||||
test_util.actions.save()
|
||||
test_util.actions.open({ '--trash', tmpdir.path })
|
||||
vim.api.nvim_feedkeys('onew_file.txt', 'x', true)
|
||||
test_util.actions.save()
|
||||
test_util.actions.reload()
|
||||
assert.are.same({ 'a.txt' }, test_util.parse_entries(0))
|
||||
end)
|
||||
|
||||
it('cannot rename files in the trash', function()
|
||||
tmpdir:create({ 'a.txt' })
|
||||
test_util.actions.open({ tmpdir.path })
|
||||
test_util.actions.focus('a.txt')
|
||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
||||
test_util.actions.save()
|
||||
test_util.actions.open({ '--trash', tmpdir.path })
|
||||
vim.api.nvim_feedkeys('0facwnew_name', 'x', true)
|
||||
test_util.actions.save()
|
||||
test_util.actions.reload()
|
||||
assert.are.same({ 'a.txt' }, test_util.parse_entries(0))
|
||||
end)
|
||||
|
||||
it('cannot copy files in the trash', function()
|
||||
tmpdir:create({ 'a.txt' })
|
||||
test_util.actions.open({ tmpdir.path })
|
||||
test_util.actions.focus('a.txt')
|
||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
||||
test_util.actions.save()
|
||||
test_util.actions.open({ '--trash', tmpdir.path })
|
||||
vim.api.nvim_feedkeys('yypp', 'x', true)
|
||||
test_util.actions.save()
|
||||
test_util.actions.reload()
|
||||
assert.are.same({ 'a.txt' }, test_util.parse_entries(0))
|
||||
end)
|
||||
|
||||
it('can restore files from trash', function()
|
||||
tmpdir:create({ 'a.txt' })
|
||||
test_util.actions.open({ tmpdir.path })
|
||||
test_util.actions.focus('a.txt')
|
||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
||||
test_util.actions.save()
|
||||
test_util.actions.open({ '--trash', tmpdir.path })
|
||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
||||
test_util.actions.open({ tmpdir.path })
|
||||
vim.api.nvim_feedkeys('p', 'x', true)
|
||||
test_util.actions.save()
|
||||
test_util.actions.reload()
|
||||
assert.are.same({ 'a.txt' }, test_util.parse_entries(0))
|
||||
tmpdir:assert_fs({
|
||||
['a.txt'] = 'a.txt',
|
||||
})
|
||||
end)
|
||||
|
||||
it('can have multiple files with the same name in trash', function()
|
||||
tmpdir:create({ 'a.txt' })
|
||||
test_util.actions.open({ tmpdir.path })
|
||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
||||
test_util.actions.save()
|
||||
tmpdir:create({ 'a.txt' })
|
||||
test_util.actions.reload()
|
||||
vim.api.nvim_feedkeys('dd', 'x', true)
|
||||
test_util.actions.save()
|
||||
test_util.actions.open({ '--trash', tmpdir.path })
|
||||
assert.are.same({ 'a.txt', 'a.txt' }, test_util.parse_entries(0))
|
||||
end)
|
||||
end)
|
||||
25
spec/url_spec.lua
Normal file
25
spec/url_spec.lua
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
local oil = require('oil')
|
||||
local util = require('oil.util')
|
||||
describe('url', function()
|
||||
it('get_url_for_path', function()
|
||||
local cases = {
|
||||
{ '', 'oil://' .. util.addslash(vim.fn.getcwd()) },
|
||||
{ 'term://~/oil.nvim//52953:/bin/sh', 'oil://' .. vim.loop.os_homedir() .. '/oil.nvim/' },
|
||||
{ '/foo/bar.txt', 'oil:///foo/', 'bar.txt' },
|
||||
{ 'oil:///foo/bar.txt', 'oil:///foo/', 'bar.txt' },
|
||||
{ 'oil:///', 'oil:///' },
|
||||
{ 'oil-ssh://user@hostname:8888//bar.txt', 'oil-ssh://user@hostname:8888//', 'bar.txt' },
|
||||
{ 'oil-ssh://user@hostname:8888//', 'oil-ssh://user@hostname:8888//' },
|
||||
}
|
||||
for _, case in ipairs(cases) do
|
||||
local input, expected, expected_basename = unpack(case)
|
||||
local output, basename = oil.get_buffer_parent_url(input, true)
|
||||
assert.equals(expected, output, string.format('Parent url for path "%s" failed', input))
|
||||
assert.equals(
|
||||
expected_basename,
|
||||
basename,
|
||||
string.format('Basename for path "%s" failed', input)
|
||||
)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
29
spec/util_spec.lua
Normal file
29
spec/util_spec.lua
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
local util = require('oil.util')
|
||||
describe('util', function()
|
||||
it('url_escape', function()
|
||||
local cases = {
|
||||
{ 'foobar', 'foobar' },
|
||||
{ 'foo bar', 'foo%20bar' },
|
||||
{ '/foo/bar', '%2Ffoo%2Fbar' },
|
||||
}
|
||||
for _, case in ipairs(cases) do
|
||||
local input, expected = unpack(case)
|
||||
local output = util.url_escape(input)
|
||||
assert.equals(expected, output)
|
||||
end
|
||||
end)
|
||||
|
||||
it('url_unescape', function()
|
||||
local cases = {
|
||||
{ 'foobar', 'foobar' },
|
||||
{ 'foo%20bar', 'foo bar' },
|
||||
{ '%2Ffoo%2Fbar', '/foo/bar' },
|
||||
{ 'foo%%bar', 'foo%%bar' },
|
||||
}
|
||||
for _, case in ipairs(cases) do
|
||||
local input, expected = unpack(case)
|
||||
local output = util.url_unescape(input)
|
||||
assert.equals(expected, output)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
65
spec/win_options_spec.lua
Normal file
65
spec/win_options_spec.lua
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
local oil = require('oil')
|
||||
local test_util = require('spec.test_util')
|
||||
|
||||
describe('window options', function()
|
||||
after_each(function()
|
||||
test_util.reset_editor()
|
||||
end)
|
||||
|
||||
it('Restores window options on close', function()
|
||||
vim.cmd.edit({ args = { 'README.md' } })
|
||||
test_util.oil_open()
|
||||
assert.equals('no', vim.o.signcolumn)
|
||||
oil.close()
|
||||
assert.equals('auto', vim.o.signcolumn)
|
||||
end)
|
||||
|
||||
it('Restores window options on edit', function()
|
||||
test_util.oil_open()
|
||||
assert.equals('no', vim.o.signcolumn)
|
||||
vim.cmd.edit({ args = { 'README.md' } })
|
||||
assert.equals('auto', vim.o.signcolumn)
|
||||
end)
|
||||
|
||||
it('Restores window options on split <filename>', function()
|
||||
test_util.oil_open()
|
||||
assert.equals('no', vim.o.signcolumn)
|
||||
vim.cmd.split({ args = { 'README.md' } })
|
||||
assert.equals('auto', vim.o.signcolumn)
|
||||
end)
|
||||
|
||||
it('Restores window options on split', function()
|
||||
test_util.oil_open()
|
||||
assert.equals('no', vim.o.signcolumn)
|
||||
vim.cmd.split()
|
||||
vim.cmd.edit({ args = { 'README.md' } })
|
||||
assert.equals('auto', vim.o.signcolumn)
|
||||
end)
|
||||
|
||||
it('Restores window options on tabnew <filename>', function()
|
||||
test_util.oil_open()
|
||||
assert.equals('no', vim.o.signcolumn)
|
||||
vim.cmd.tabnew({ args = { 'README.md' } })
|
||||
assert.equals('auto', vim.o.signcolumn)
|
||||
end)
|
||||
|
||||
it('Restores window options on tabnew', function()
|
||||
test_util.oil_open()
|
||||
assert.equals('no', vim.o.signcolumn)
|
||||
vim.cmd.tabnew()
|
||||
vim.cmd.edit({ args = { 'README.md' } })
|
||||
assert.equals('auto', vim.o.signcolumn)
|
||||
end)
|
||||
|
||||
it('Sets the window options when re-entering oil buffer', function()
|
||||
oil.open()
|
||||
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
|
||||
assert.truthy(vim.w.oil_did_enter)
|
||||
vim.cmd.edit({ args = { 'README.md' } })
|
||||
assert.falsy(vim.w.oil_did_enter)
|
||||
oil.open()
|
||||
assert.truthy(vim.w.oil_did_enter)
|
||||
vim.cmd.vsplit()
|
||||
assert.truthy(vim.w.oil_did_enter)
|
||||
end)
|
||||
end)
|
||||
Loading…
Add table
Add a link
Reference in a new issue