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:
Barrett Ruth 2026-02-22 00:26:54 -05:00
parent a4da206b67
commit 6be0148eef
Signed by: barrett
GPG key ID: A6C96C9349D2FC81
28 changed files with 257 additions and 298 deletions

9
.busted Normal file
View file

@ -0,0 +1,9 @@
return {
_all = {
lua = "nlua",
},
default = {
ROOT = { "./spec/" },
helper = "spec/minimal_init.lua",
},
}

View file

@ -1,16 +0,0 @@
#!/bin/bash
set -e
version="${NVIM_TAG-stable}"
dl_name="nvim-linux-x86_64.appimage"
# The appimage name changed in v0.10.4
if python -c 'from packaging.version import Version; import sys; sys.exit(not (Version(sys.argv[1]) < Version("v0.10.4")))' "$version" 2>/dev/null; then
dl_name="nvim.appimage"
fi
curl -sL "https://github.com/neovim/neovim/releases/download/${version}/${dl_name}" -o nvim.appimage
chmod +x nvim.appimage
./nvim.appimage --appimage-extract >/dev/null
rm -f nvim.appimage
mkdir -p ~/.local/share/nvim
mv squashfs-root ~/.local/share/nvim/appimage
sudo ln -s "$HOME/.local/share/nvim/appimage/AppRun" /usr/bin/nvim
/usr/bin/nvim --version

View file

@ -12,7 +12,7 @@ on:
jobs: jobs:
selene: selene:
name: Selene name: Selene
runs-on: ubuntu-22.04 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: NTBBloodbath/selene-action@v1.0.0 - uses: NTBBloodbath/selene-action@v1.0.0
@ -22,7 +22,7 @@ jobs:
stylua: stylua:
name: StyLua name: StyLua
runs-on: ubuntu-22.04 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Stylua - name: Stylua
@ -30,11 +30,11 @@ jobs:
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
version: v2.1.0 version: v2.1.0
args: --check lua tests args: --check lua spec
typecheck: typecheck:
name: typecheck name: typecheck
runs-on: ubuntu-22.04 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: mrcjkb/lua-typecheck-action@v0 - uses: mrcjkb/lua-typecheck-action@v0
@ -45,24 +45,16 @@ jobs:
run_tests: run_tests:
strategy: strategy:
fail-fast: false
matrix: matrix:
include: nvim_version:
- nvim_tag: v0.8.3 - stable
- nvim_tag: v0.9.4 - nightly
- nvim_tag: v0.10.4
- nvim_tag: v0.11.0
name: Run tests name: Run tests
runs-on: ubuntu-22.04 runs-on: ubuntu-latest
env:
NVIM_TAG: ${{ matrix.nvim_tag }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: nvim-neorocks/nvim-busted-action@v1
- name: Install Neovim and dependencies with:
run: | nvim_version: ${{ matrix.nvim_version }}
bash ./.github/workflows/install_nvim.sh
- name: Run tests
run: |
bash ./run_tests.sh

1
.gitignore vendored
View file

@ -41,7 +41,6 @@ luac.out
.direnv/ .direnv/
.envrc .envrc
.testenv/
doc/tags doc/tags
scripts/benchmark.nvim scripts/benchmark.nvim
perf/tmp/ perf/tmp/

View file

@ -11,13 +11,13 @@ all: lint test
## test: run tests ## test: run tests
.PHONY: test .PHONY: test
test: test:
./run_tests.sh luarocks test --local
## lint: run selene and stylua ## lint: run selene and stylua
.PHONY: lint .PHONY: lint
lint: lint:
selene --display-style quiet . selene --display-style quiet .
stylua --check lua tests stylua --check lua spec
## profile: use LuaJIT profiler to profile the plugin ## profile: use LuaJIT profiler to profile the plugin
.PHONY: profile .PHONY: profile
@ -41,4 +41,4 @@ scripts/benchmark.nvim:
## clean: reset the repository to a clean state ## clean: reset the repository to a clean state
.PHONY: clean .PHONY: clean
clean: clean:
rm -rf .testenv perf/tmp profile.json rm -rf perf/tmp profile.json

View file

@ -22,6 +22,10 @@
pkgs.prettier pkgs.prettier
pkgs.stylua pkgs.stylua
pkgs.selene pkgs.selene
(pkgs.luajit.withPackages (ps: [
ps.busted
ps.nlua
]))
]; ];
}; };
}); });

View file

@ -16,6 +16,15 @@ dependencies = {
'lua >= 5.1', 'lua >= 5.1',
} }
test_dependencies = {
'nlua',
'busted >= 2.1.1',
}
test = {
type = 'busted',
}
build = { build = {
type = 'builtin', type = 'builtin',
} }

View file

@ -1,24 +0,0 @@
#!/usr/bin/env bash
set -e
mkdir -p ".testenv/config/nvim"
mkdir -p ".testenv/data/nvim"
mkdir -p ".testenv/state/nvim"
mkdir -p ".testenv/run/nvim"
mkdir -p ".testenv/cache/nvim"
PLUGINS=".testenv/data/nvim/site/pack/plugins/start"
if [ ! -e "$PLUGINS/plenary.nvim" ]; then
git clone --depth=1 https://github.com/nvim-lua/plenary.nvim.git "$PLUGINS/plenary.nvim"
else
(cd "$PLUGINS/plenary.nvim" && git pull)
fi
XDG_CONFIG_HOME=".testenv/config" \
XDG_DATA_HOME=".testenv/data" \
XDG_STATE_HOME=".testenv/state" \
XDG_RUNTIME_DIR=".testenv/run" \
XDG_CACHE_HOME=".testenv/cache" \
nvim --headless -u tests/minimal_init.lua \
-c "PlenaryBustedDirectory ${1-tests} { minimal_init = './tests/minimal_init.lua' }"
echo "Success"

View file

@ -1,14 +1,13 @@
require('plenary.async').tests.add_to_env()
local fs = require('oil.fs') local fs = require('oil.fs')
local oil = require('oil') local oil = require('oil')
local test_util = require('tests.test_util') local test_util = require('spec.test_util')
a.describe('Alternate buffer', function() describe('Alternate buffer', function()
after_each(function() after_each(function()
test_util.reset_editor() test_util.reset_editor()
end) end)
a.it('sets previous buffer as alternate', function() it('sets previous buffer as alternate', function()
vim.cmd.edit({ args = { 'foo' } }) vim.cmd.edit({ args = { 'foo' } })
oil.open() oil.open()
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' }) test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
@ -16,20 +15,19 @@ a.describe('Alternate buffer', function()
assert.equals('foo', vim.fn.expand('#')) assert.equals('foo', vim.fn.expand('#'))
end) end)
a.it('sets previous buffer as alternate when editing url file', function() it('sets previous buffer as alternate when editing url file', function()
vim.cmd.edit({ args = { 'foo' } }) vim.cmd.edit({ args = { 'foo' } })
oil.open() oil.open()
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' }) test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
local readme = fs.join(vim.fn.getcwd(), 'README.md') local readme = fs.join(vim.fn.getcwd(), 'README.md')
vim.cmd.edit({ args = { 'oil://' .. fs.os_to_posix_path(readme) } }) vim.cmd.edit({ args = { 'oil://' .. fs.os_to_posix_path(readme) } })
-- We're gonna jump around to 2 different buffers
test_util.wait_for_autocmd('BufEnter') test_util.wait_for_autocmd('BufEnter')
test_util.wait_for_autocmd('BufEnter') test_util.wait_for_autocmd('BufEnter')
assert.equals(readme, vim.api.nvim_buf_get_name(0)) assert.equals(readme, vim.api.nvim_buf_get_name(0))
assert.equals('foo', vim.fn.expand('#')) assert.equals('foo', vim.fn.expand('#'))
end) end)
a.it('sets previous buffer as alternate when editing oil://', function() it('sets previous buffer as alternate when editing oil://', function()
vim.cmd.edit({ args = { 'foo' } }) vim.cmd.edit({ args = { 'foo' } })
vim.cmd.edit({ args = { 'oil://' .. fs.os_to_posix_path(vim.fn.getcwd()) } }) vim.cmd.edit({ args = { 'oil://' .. fs.os_to_posix_path(vim.fn.getcwd()) } })
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' }) test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
@ -37,7 +35,7 @@ a.describe('Alternate buffer', function()
assert.equals('foo', vim.fn.expand('#')) assert.equals('foo', vim.fn.expand('#'))
end) end)
a.it('preserves alternate buffer if editing the same file', function() it('preserves alternate buffer if editing the same file', function()
vim.cmd.edit({ args = { 'foo' } }) vim.cmd.edit({ args = { 'foo' } })
vim.cmd.edit({ args = { 'bar' } }) vim.cmd.edit({ args = { 'bar' } })
oil.open() oil.open()
@ -46,7 +44,7 @@ a.describe('Alternate buffer', function()
assert.equals('foo', vim.fn.expand('#')) assert.equals('foo', vim.fn.expand('#'))
end) end)
a.it('preserves alternate buffer if discarding changes', function() it('preserves alternate buffer if discarding changes', function()
vim.cmd.edit({ args = { 'foo' } }) vim.cmd.edit({ args = { 'foo' } })
vim.cmd.edit({ args = { 'bar' } }) vim.cmd.edit({ args = { 'bar' } })
oil.open() oil.open()
@ -56,7 +54,7 @@ a.describe('Alternate buffer', function()
assert.equals('foo', vim.fn.expand('#')) assert.equals('foo', vim.fn.expand('#'))
end) end)
a.it('sets previous buffer as alternate after multi-dir hops', function() it('sets previous buffer as alternate after multi-dir hops', function()
vim.cmd.edit({ args = { 'foo' } }) vim.cmd.edit({ args = { 'foo' } })
oil.open() oil.open()
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' }) test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
@ -70,7 +68,7 @@ a.describe('Alternate buffer', function()
assert.equals('foo', vim.fn.expand('#')) assert.equals('foo', vim.fn.expand('#'))
end) end)
a.it('sets previous buffer as alternate when inside oil buffer', function() it('sets previous buffer as alternate when inside oil buffer', function()
vim.cmd.edit({ args = { 'foo' } }) vim.cmd.edit({ args = { 'foo' } })
oil.open() oil.open()
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' }) test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
@ -81,7 +79,7 @@ a.describe('Alternate buffer', function()
assert.equals('bar', vim.fn.expand('#')) assert.equals('bar', vim.fn.expand('#'))
end) end)
a.it('preserves alternate when traversing oil dirs', function() it('preserves alternate when traversing oil dirs', function()
vim.cmd.edit({ args = { 'foo' } }) vim.cmd.edit({ args = { 'foo' } })
oil.open() oil.open()
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' }) test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
@ -95,7 +93,7 @@ a.describe('Alternate buffer', function()
assert.equals('foo', vim.fn.expand('#')) assert.equals('foo', vim.fn.expand('#'))
end) end)
a.it('preserves alternate when opening preview', function() it('preserves alternate when opening preview', function()
vim.cmd.edit({ args = { 'foo' } }) vim.cmd.edit({ args = { 'foo' } })
oil.open() oil.open()
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' }) test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
@ -109,31 +107,27 @@ a.describe('Alternate buffer', function()
assert.equals('foo', vim.fn.expand('#')) assert.equals('foo', vim.fn.expand('#'))
end) end)
a.describe('floating window', function() describe('floating window', function()
a.it('sets previous buffer as alternate', function() it('sets previous buffer as alternate', function()
vim.cmd.edit({ args = { 'foo' } }) vim.cmd.edit({ args = { 'foo' } })
oil.open_float() oil.open_float()
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' }) test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
-- This is lazy, but testing the actual select logic is more difficult. We can simply
-- replicate it by closing the current window and then doing the edit
vim.api.nvim_win_close(0, true) vim.api.nvim_win_close(0, true)
vim.cmd.edit({ args = { 'bar' } }) vim.cmd.edit({ args = { 'bar' } })
assert.equals('foo', vim.fn.expand('#')) assert.equals('foo', vim.fn.expand('#'))
end) end)
a.it('preserves alternate buffer if editing the same file', function() it('preserves alternate buffer if editing the same file', function()
vim.cmd.edit({ args = { 'foo' } }) vim.cmd.edit({ args = { 'foo' } })
vim.cmd.edit({ args = { 'bar' } }) vim.cmd.edit({ args = { 'bar' } })
oil.open_float() oil.open_float()
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' }) test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
-- This is lazy, but testing the actual select logic is more difficult. We can simply
-- replicate it by closing the current window and then doing the edit
vim.api.nvim_win_close(0, true) vim.api.nvim_win_close(0, true)
vim.cmd.edit({ args = { 'bar' } }) vim.cmd.edit({ args = { 'bar' } })
assert.equals('foo', vim.fn.expand('#')) assert.equals('foo', vim.fn.expand('#'))
end) end)
a.it('preserves alternate buffer if discarding changes', function() it('preserves alternate buffer if discarding changes', function()
vim.cmd.edit({ args = { 'foo' } }) vim.cmd.edit({ args = { 'foo' } })
vim.cmd.edit({ args = { 'bar' } }) vim.cmd.edit({ args = { 'bar' } })
oil.open_float() oil.open_float()
@ -142,7 +136,7 @@ a.describe('Alternate buffer', function()
assert.equals('foo', vim.fn.expand('#')) assert.equals('foo', vim.fn.expand('#'))
end) end)
a.it('preserves alternate when traversing to a new file', function() it('preserves alternate when traversing to a new file', function()
vim.cmd.edit({ args = { 'foo' } }) vim.cmd.edit({ args = { 'foo' } })
oil.open_float() oil.open_float()
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' }) test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })

View file

@ -1,16 +1,15 @@
require('plenary.async').tests.add_to_env()
local oil = require('oil') local oil = require('oil')
local test_util = require('tests.test_util') local test_util = require('spec.test_util')
a.describe('close', function() describe('close', function()
a.before_each(function() before_each(function()
test_util.reset_editor() test_util.reset_editor()
end) end)
a.after_each(function() after_each(function()
test_util.reset_editor() test_util.reset_editor()
end) end)
a.it('does not close buffer from visual mode', function() it('does not close buffer from visual mode', function()
oil.open() oil.open()
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' }) test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
assert.equals('oil', vim.bo.filetype) assert.equals('oil', vim.bo.filetype)
@ -20,22 +19,22 @@ a.describe('close', function()
test_util.feedkeys({ '<Esc>' }, 10) test_util.feedkeys({ '<Esc>' }, 10)
end) end)
a.it('does not close buffer from operator-pending mode', function() it('does not close buffer from operator-pending mode', function()
oil.open() oil.open()
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' }) test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
assert.equals('oil', vim.bo.filetype) assert.equals('oil', vim.bo.filetype)
vim.api.nvim_feedkeys('d', 'n', false) vim.api.nvim_feedkeys('d', 'n', false)
a.util.sleep(20) vim.wait(20)
local mode = vim.api.nvim_get_mode().mode local mode = vim.api.nvim_get_mode().mode
if mode:match('^no') then if mode:match('^no') then
oil.close() oil.close()
assert.equals('oil', vim.bo.filetype) assert.equals('oil', vim.bo.filetype)
end end
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('<Esc>', true, true, true), 'n', false) vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('<Esc>', true, true, true), 'n', false)
a.util.sleep(20) vim.wait(20)
end) end)
a.it('closes buffer from normal mode', function() it('closes buffer from normal mode', function()
oil.open() oil.open()
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' }) test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
assert.equals('oil', vim.bo.filetype) assert.equals('oil', vim.bo.filetype)

View file

@ -1,21 +1,20 @@
require('plenary.async').tests.add_to_env() local TmpDir = require('spec.tmpdir')
local TmpDir = require('tests.tmpdir')
local files = require('oil.adapters.files') local files = require('oil.adapters.files')
local test_util = require('tests.test_util') local test_util = require('spec.test_util')
a.describe('files adapter', function() describe('files adapter', function()
local tmpdir local tmpdir
a.before_each(function() before_each(function()
tmpdir = TmpDir.new() tmpdir = TmpDir.new()
end) end)
a.after_each(function() after_each(function()
if tmpdir then if tmpdir then
tmpdir:dispose() tmpdir:dispose()
end end
test_util.reset_editor() test_util.reset_editor()
end) end)
a.it('tmpdir creates files and asserts they exist', function() it('tmpdir creates files and asserts they exist', function()
tmpdir:create({ 'a.txt', 'foo/b.txt', 'foo/c.txt', 'bar/' }) tmpdir:create({ 'a.txt', 'foo/b.txt', 'foo/c.txt', 'bar/' })
tmpdir:assert_fs({ tmpdir:assert_fs({
['a.txt'] = 'a.txt', ['a.txt'] = 'a.txt',
@ -25,8 +24,8 @@ a.describe('files adapter', function()
}) })
end) end)
a.it('Creates files', function() it('Creates files', function()
local err = a.wrap(files.perform_action, 2)({ local err = test_util.await(files.perform_action, 2, {
url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a.txt', url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a.txt',
entry_type = 'file', entry_type = 'file',
type = 'create', type = 'create',
@ -37,8 +36,8 @@ a.describe('files adapter', function()
}) })
end) end)
a.it('Creates directories', function() it('Creates directories', function()
local err = a.wrap(files.perform_action, 2)({ local err = test_util.await(files.perform_action, 2, {
url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a', url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a',
entry_type = 'directory', entry_type = 'directory',
type = 'create', type = 'create',
@ -49,11 +48,10 @@ a.describe('files adapter', function()
}) })
end) end)
a.it('Deletes files', function() it('Deletes files', function()
tmpdir:create({ 'a.txt' }) tmpdir:create({ 'a.txt' })
a.util.scheduler()
local url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a.txt' local url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a.txt'
local err = a.wrap(files.perform_action, 2)({ local err = test_util.await(files.perform_action, 2, {
url = url, url = url,
entry_type = 'file', entry_type = 'file',
type = 'delete', type = 'delete',
@ -62,10 +60,10 @@ a.describe('files adapter', function()
tmpdir:assert_fs({}) tmpdir:assert_fs({})
end) end)
a.it('Deletes directories', function() it('Deletes directories', function()
tmpdir:create({ 'a/' }) tmpdir:create({ 'a/' })
local url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a' local url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a'
local err = a.wrap(files.perform_action, 2)({ local err = test_util.await(files.perform_action, 2, {
url = url, url = url,
entry_type = 'directory', entry_type = 'directory',
type = 'delete', type = 'delete',
@ -74,12 +72,11 @@ a.describe('files adapter', function()
tmpdir:assert_fs({}) tmpdir:assert_fs({})
end) end)
a.it('Moves files', function() it('Moves files', function()
tmpdir:create({ 'a.txt' }) tmpdir:create({ 'a.txt' })
a.util.scheduler()
local src_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. '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 dest_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'b.txt'
local err = a.wrap(files.perform_action, 2)({ local err = test_util.await(files.perform_action, 2, {
src_url = src_url, src_url = src_url,
dest_url = dest_url, dest_url = dest_url,
entry_type = 'file', entry_type = 'file',
@ -91,12 +88,11 @@ a.describe('files adapter', function()
}) })
end) end)
a.it('Moves directories', function() it('Moves directories', function()
tmpdir:create({ 'a/a.txt' }) tmpdir:create({ 'a/a.txt' })
a.util.scheduler()
local src_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a' local src_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a'
local dest_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'b' local dest_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'b'
local err = a.wrap(files.perform_action, 2)({ local err = test_util.await(files.perform_action, 2, {
src_url = src_url, src_url = src_url,
dest_url = dest_url, dest_url = dest_url,
entry_type = 'directory', entry_type = 'directory',
@ -109,12 +105,11 @@ a.describe('files adapter', function()
}) })
end) end)
a.it('Copies files', function() it('Copies files', function()
tmpdir:create({ 'a.txt' }) tmpdir:create({ 'a.txt' })
a.util.scheduler()
local src_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. '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 dest_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'b.txt'
local err = a.wrap(files.perform_action, 2)({ local err = test_util.await(files.perform_action, 2, {
src_url = src_url, src_url = src_url,
dest_url = dest_url, dest_url = dest_url,
entry_type = 'file', entry_type = 'file',
@ -127,12 +122,11 @@ a.describe('files adapter', function()
}) })
end) end)
a.it('Recursively copies directories', function() it('Recursively copies directories', function()
tmpdir:create({ 'a/a.txt' }) tmpdir:create({ 'a/a.txt' })
a.util.scheduler()
local src_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a' local src_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'a'
local dest_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'b' local dest_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. 'b'
local err = a.wrap(files.perform_action, 2)({ local err = test_util.await(files.perform_action, 2, {
src_url = src_url, src_url = src_url,
dest_url = dest_url, dest_url = dest_url,
entry_type = 'directory', entry_type = 'directory',
@ -147,7 +141,7 @@ a.describe('files adapter', function()
}) })
end) end)
a.it('Editing a new oil://path/ creates an oil buffer', function() it('Editing a new oil://path/ creates an oil buffer', function()
local tmpdir_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. '/' local tmpdir_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. '/'
vim.cmd.edit({ args = { tmpdir_url } }) vim.cmd.edit({ args = { tmpdir_url } })
test_util.wait_oil_ready() test_util.wait_oil_ready()
@ -155,11 +149,10 @@ a.describe('files adapter', function()
vim.cmd.edit({ args = { new_url } }) vim.cmd.edit({ args = { new_url } })
test_util.wait_oil_ready() test_util.wait_oil_ready()
assert.equals('oil', vim.bo.filetype) assert.equals('oil', vim.bo.filetype)
-- The normalization will add a '/'
assert.equals(new_url .. '/', vim.api.nvim_buf_get_name(0)) assert.equals(new_url .. '/', vim.api.nvim_buf_get_name(0))
end) end)
a.it('Editing a new oil://file.rb creates a normal buffer', function() it('Editing a new oil://file.rb creates a normal buffer', function()
local tmpdir_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. '/' local tmpdir_url = 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') .. '/'
vim.cmd.edit({ args = { tmpdir_url } }) vim.cmd.edit({ args = { tmpdir_url } })
test_util.wait_for_autocmd('BufReadPost') test_util.wait_for_autocmd('BufReadPost')

8
spec/minimal_init.lua Normal file
View 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()

View file

@ -1,5 +1,5 @@
local fs = require('oil.fs') local fs = require('oil.fs')
local test_util = require('tests.test_util') local test_util = require('spec.test_util')
local util = require('oil.util') local util = require('oil.util')
describe('update_moved_buffers', function() describe('update_moved_buffers', function()

View file

@ -1,15 +1,14 @@
require('plenary.async').tests.add_to_env()
local cache = require('oil.cache') local cache = require('oil.cache')
local constants = require('oil.constants') local constants = require('oil.constants')
local mutator = require('oil.mutator') local mutator = require('oil.mutator')
local test_adapter = require('oil.adapters.test') local test_adapter = require('oil.adapters.test')
local test_util = require('tests.test_util') local test_util = require('spec.test_util')
local FIELD_ID = constants.FIELD_ID local FIELD_ID = constants.FIELD_ID
local FIELD_NAME = constants.FIELD_NAME local FIELD_NAME = constants.FIELD_NAME
local FIELD_TYPE = constants.FIELD_TYPE local FIELD_TYPE = constants.FIELD_TYPE
a.describe('mutator', function() describe('mutator', function()
after_each(function() after_each(function()
test_util.reset_editor() test_util.reset_editor()
end) end)
@ -196,9 +195,6 @@ a.describe('mutator', function()
end) end)
it('Handles parent child move ordering', function() it('Handles parent child move ordering', function()
-- move parent into a child and child OUT of parent
-- MOVE /a/b -> /b
-- MOVE /a -> /b/a
local move1 = { local move1 = {
type = 'move', type = 'move',
src_url = 'oil-test:///a/b', src_url = 'oil-test:///a/b',
@ -217,9 +213,6 @@ a.describe('mutator', function()
end) end)
it('Handles a delete inside a moved folder', function() it('Handles a delete inside a moved folder', function()
-- delete in directory and move directory
-- DELETE /a/b.txt
-- MOVE /a/ -> /b/
local del = { local del = {
type = 'delete', type = 'delete',
url = 'oil-test:///a/b.txt', url = 'oil-test:///a/b.txt',
@ -346,12 +339,12 @@ a.describe('mutator', function()
end) end)
end) end)
a.describe('perform actions', function() describe('perform actions', function()
a.it('creates new entries', function() it('creates new entries', function()
local actions = { local actions = {
{ type = 'create', url = 'oil-test:///a.txt', entry_type = 'file' }, { type = 'create', url = 'oil-test:///a.txt', entry_type = 'file' },
} }
a.wrap(mutator.process_actions, 2)(actions) test_util.await(mutator.process_actions, 2, actions)
local files = cache.list_url('oil-test:///') local files = cache.list_url('oil-test:///')
assert.are.same({ assert.are.same({
['a.txt'] = { ['a.txt'] = {
@ -362,12 +355,12 @@ a.describe('mutator', function()
}, files) }, files)
end) end)
a.it('deletes entries', function() it('deletes entries', function()
local file = test_adapter.test_set('/a.txt', 'file') local file = test_adapter.test_set('/a.txt', 'file')
local actions = { local actions = {
{ type = 'delete', url = 'oil-test:///a.txt', entry_type = 'file' }, { type = 'delete', url = 'oil-test:///a.txt', entry_type = 'file' },
} }
a.wrap(mutator.process_actions, 2)(actions) test_util.await(mutator.process_actions, 2, actions)
local files = cache.list_url('oil-test:///') local files = cache.list_url('oil-test:///')
assert.are.same({}, files) assert.are.same({}, files)
assert.is_nil(cache.get_entry_by_id(file[FIELD_ID])) assert.is_nil(cache.get_entry_by_id(file[FIELD_ID]))
@ -376,7 +369,7 @@ a.describe('mutator', function()
end) end)
end) end)
a.it('moves entries', function() it('moves entries', function()
local file = test_adapter.test_set('/a.txt', 'file') local file = test_adapter.test_set('/a.txt', 'file')
local actions = { local actions = {
{ {
@ -386,7 +379,7 @@ a.describe('mutator', function()
entry_type = 'file', entry_type = 'file',
}, },
} }
a.wrap(mutator.process_actions, 2)(actions) test_util.await(mutator.process_actions, 2, actions)
local files = cache.list_url('oil-test:///') local files = cache.list_url('oil-test:///')
local new_entry = { local new_entry = {
[FIELD_ID] = file[FIELD_ID], [FIELD_ID] = file[FIELD_ID],
@ -400,7 +393,7 @@ a.describe('mutator', function()
assert.equals('oil-test:///', cache.get_parent_url(file[FIELD_ID])) assert.equals('oil-test:///', cache.get_parent_url(file[FIELD_ID]))
end) end)
a.it('copies entries', function() it('copies entries', function()
local file = test_adapter.test_set('/a.txt', 'file') local file = test_adapter.test_set('/a.txt', 'file')
local actions = { local actions = {
{ {
@ -410,7 +403,7 @@ a.describe('mutator', function()
entry_type = 'file', entry_type = 'file',
}, },
} }
a.wrap(mutator.process_actions, 2)(actions) test_util.await(mutator.process_actions, 2, actions)
local files = cache.list_url('oil-test:///') local files = cache.list_url('oil-test:///')
local new_entry = { local new_entry = {
[FIELD_ID] = file[FIELD_ID] + 1, [FIELD_ID] = file[FIELD_ID] + 1,

View file

@ -1,8 +1,7 @@
require('plenary.async').tests.add_to_env()
local constants = require('oil.constants') local constants = require('oil.constants')
local parser = require('oil.mutator.parser') local parser = require('oil.mutator.parser')
local test_adapter = require('oil.adapters.test') local test_adapter = require('oil.adapters.test')
local test_util = require('tests.test_util') local test_util = require('spec.test_util')
local util = require('oil.util') local util = require('oil.util')
local view = require('oil.view') local view = require('oil.view')

View file

@ -1,25 +1,24 @@
require('plenary.async').tests.add_to_env() local TmpDir = require('spec.tmpdir')
local TmpDir = require('tests.tmpdir')
local oil = require('oil') local oil = require('oil')
local test_util = require('tests.test_util') local test_util = require('spec.test_util')
local util = require('oil.util') local util = require('oil.util')
a.describe('oil preview', function() describe('oil preview', function()
local tmpdir local tmpdir
a.before_each(function() before_each(function()
tmpdir = TmpDir.new() tmpdir = TmpDir.new()
end) end)
a.after_each(function() after_each(function()
if tmpdir then if tmpdir then
tmpdir:dispose() tmpdir:dispose()
end end
test_util.reset_editor() test_util.reset_editor()
end) end)
a.it('opens preview window', function() it('opens preview window', function()
tmpdir:create({ 'a.txt' }) tmpdir:create({ 'a.txt' })
test_util.oil_open(tmpdir.path) test_util.oil_open(tmpdir.path)
a.wrap(oil.open_preview, 2)() test_util.await(oil.open_preview, 2)
local preview_win = util.get_preview_win() local preview_win = util.get_preview_win()
assert.not_nil(preview_win) assert.not_nil(preview_win)
assert(preview_win) assert(preview_win)
@ -28,7 +27,7 @@ a.describe('oil preview', function()
assert.are.same({ 'a.txt' }, preview_lines) assert.are.same({ 'a.txt' }, preview_lines)
end) end)
a.it('opens preview window when open(preview={})', function() it('opens preview window when open(preview={})', function()
tmpdir:create({ 'a.txt' }) tmpdir:create({ 'a.txt' })
test_util.oil_open(tmpdir.path, { preview = {} }) test_util.oil_open(tmpdir.path, { preview = {} })
local preview_win = util.get_preview_win() local preview_win = util.get_preview_win()

View file

@ -1,16 +1,15 @@
require('plenary.async').tests.add_to_env() local TmpDir = require('spec.tmpdir')
local TmpDir = require('tests.tmpdir')
local actions = require('oil.actions') local actions = require('oil.actions')
local oil = require('oil') local oil = require('oil')
local test_util = require('tests.test_util') local test_util = require('spec.test_util')
local view = require('oil.view') local view = require('oil.view')
a.describe('regression tests', function() describe('regression tests', function()
local tmpdir local tmpdir
a.before_each(function() before_each(function()
tmpdir = TmpDir.new() tmpdir = TmpDir.new()
end) end)
a.after_each(function() after_each(function()
if tmpdir then if tmpdir then
tmpdir:dispose() tmpdir:dispose()
tmpdir = nil tmpdir = nil
@ -18,8 +17,7 @@ a.describe('regression tests', function()
test_util.reset_editor() test_util.reset_editor()
end) end)
-- see https://github.com/stevearc/oil.nvim/issues/25 it('can edit dirs that will be renamed to an existing buffer', function()
a.it('can edit dirs that will be renamed to an existing buffer', function()
vim.cmd.edit({ args = { 'README.md' } }) vim.cmd.edit({ args = { 'README.md' } })
vim.cmd.vsplit() vim.cmd.vsplit()
vim.cmd.edit({ args = { '%:p:h' } }) vim.cmd.edit({ args = { '%:p:h' } })
@ -31,8 +29,7 @@ a.describe('regression tests', function()
assert.equals('oil', vim.bo.filetype) assert.equals('oil', vim.bo.filetype)
end) end)
-- https://github.com/stevearc/oil.nvim/issues/37 it('places the cursor on correct entry when opening on file', function()
a.it('places the cursor on correct entry when opening on file', function()
vim.cmd.edit({ args = { '.' } }) vim.cmd.edit({ args = { '.' } })
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' }) test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
local entry = oil.get_cursor_entry() local entry = oil.get_cursor_entry()
@ -46,8 +43,7 @@ a.describe('regression tests', function()
assert.equals('README.md', entry and entry.name) assert.equals('README.md', entry and entry.name)
end) end)
-- https://github.com/stevearc/oil.nvim/issues/64 it("doesn't close floating windows oil didn't open itself", function()
a.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, { local winid = vim.api.nvim_open_win(vim.fn.bufadd('README.md'), true, {
relative = 'editor', relative = 'editor',
row = 1, row = 1,
@ -56,29 +52,27 @@ a.describe('regression tests', function()
height = 100, height = 100,
}) })
oil.open() oil.open()
a.util.sleep(10) vim.wait(10)
oil.close() oil.close()
a.util.sleep(10) vim.wait(10)
assert.equals(winid, vim.api.nvim_get_current_win()) assert.equals(winid, vim.api.nvim_get_current_win())
end) end)
-- https://github.com/stevearc/oil.nvim/issues/64 it("doesn't close splits on oil.close", function()
a.it("doesn't close splits on oil.close", function()
vim.cmd.edit({ args = { 'README.md' } }) vim.cmd.edit({ args = { 'README.md' } })
vim.cmd.vsplit() vim.cmd.vsplit()
local winid = vim.api.nvim_get_current_win() local winid = vim.api.nvim_get_current_win()
local bufnr = vim.api.nvim_get_current_buf() local bufnr = vim.api.nvim_get_current_buf()
oil.open() oil.open()
a.util.sleep(10) vim.wait(10)
oil.close() oil.close()
a.util.sleep(10) vim.wait(10)
assert.equals(2, #vim.api.nvim_tabpage_list_wins(0)) assert.equals(2, #vim.api.nvim_tabpage_list_wins(0))
assert.equals(winid, vim.api.nvim_get_current_win()) assert.equals(winid, vim.api.nvim_get_current_win())
assert.equals(bufnr, vim.api.nvim_get_current_buf()) assert.equals(bufnr, vim.api.nvim_get_current_buf())
end) end)
-- https://github.com/stevearc/oil.nvim/issues/79 it('Returns to empty buffer on close', function()
a.it('Returns to empty buffer on close', function()
oil.open() oil.open()
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' }) test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
oil.close() oil.close()
@ -86,9 +80,8 @@ a.describe('regression tests', function()
assert.equals('', vim.api.nvim_buf_get_name(0)) assert.equals('', vim.api.nvim_buf_get_name(0))
end) end)
a.it('All buffers set nomodified after save', function() it('All buffers set nomodified after save', function()
tmpdir:create({ 'a.txt' }) tmpdir:create({ 'a.txt' })
a.util.scheduler()
vim.cmd.edit({ args = { 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') } }) vim.cmd.edit({ args = { 'oil://' .. vim.fn.fnamemodify(tmpdir.path, ':p') } })
local first_dir = vim.api.nvim_get_current_buf() local first_dir = vim.api.nvim_get_current_buf()
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' }) test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
@ -97,7 +90,6 @@ a.describe('regression tests', function()
return vim.bo.modifiable return vim.bo.modifiable
end, 10) end, 10)
test_util.feedkeys({ 'p' }, 10) test_util.feedkeys({ 'p' }, 10)
a.util.scheduler()
oil.save({ confirm = false }) oil.save({ confirm = false })
vim.wait(1000, function() vim.wait(1000, function()
return vim.bo.modifiable return vim.bo.modifiable
@ -105,11 +97,10 @@ a.describe('regression tests', function()
tmpdir:assert_fs({ tmpdir:assert_fs({
['test/a.txt'] = 'a.txt', ['test/a.txt'] = 'a.txt',
}) })
-- The first oil buffer should not be modified anymore
assert.falsy(vim.bo[first_dir].modified) assert.falsy(vim.bo[first_dir].modified)
end) end)
a.it("refreshing buffer doesn't lose track of it", function() it("refreshing buffer doesn't lose track of it", function()
vim.cmd.edit({ args = { '.' } }) vim.cmd.edit({ args = { '.' } })
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' }) test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
local bufnr = vim.api.nvim_get_current_buf() local bufnr = vim.api.nvim_get_current_buf()
@ -118,7 +109,7 @@ a.describe('regression tests', function()
assert.are.same({ bufnr }, require('oil.view').get_all_buffers()) assert.are.same({ bufnr }, require('oil.view').get_all_buffers())
end) end)
a.it('can copy a file multiple times', function() it('can copy a file multiple times', function()
test_util.actions.open({ tmpdir.path }) test_util.actions.open({ tmpdir.path })
vim.api.nvim_feedkeys('ifoo.txt', 'x', true) vim.api.nvim_feedkeys('ifoo.txt', 'x', true)
test_util.actions.save() test_util.actions.save()
@ -133,10 +124,8 @@ a.describe('regression tests', function()
}) })
end) end)
-- https://github.com/stevearc/oil.nvim/issues/355 it('can open files from floating window', function()
a.it('can open files from floating window', function()
tmpdir:create({ 'a.txt' }) tmpdir:create({ 'a.txt' })
a.util.scheduler()
oil.open_float(tmpdir.path) oil.open_float(tmpdir.path)
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' }) test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
actions.select.callback() actions.select.callback()

View file

@ -1,90 +1,85 @@
require('plenary.async').tests.add_to_env()
local oil = require('oil') local oil = require('oil')
local test_util = require('tests.test_util') local test_util = require('spec.test_util')
a.describe('oil select', function() describe('oil select', function()
after_each(function() after_each(function()
test_util.reset_editor() test_util.reset_editor()
end) end)
a.it('opens file under cursor', function() it('opens file under cursor', function()
test_util.oil_open() test_util.oil_open()
-- Go to the bottom, so the cursor is not on a directory
vim.cmd.normal({ args = { 'G' } }) vim.cmd.normal({ args = { 'G' } })
a.wrap(oil.select, 2)() test_util.await(oil.select, 2)
assert.equals(1, #vim.api.nvim_tabpage_list_wins(0)) assert.equals(1, #vim.api.nvim_tabpage_list_wins(0))
assert.not_equals('oil', vim.bo.filetype) assert.not_equals('oil', vim.bo.filetype)
end) end)
a.it('opens file in new tab', function() it('opens file in new tab', function()
test_util.oil_open() test_util.oil_open()
local tabpage = vim.api.nvim_get_current_tabpage() local tabpage = vim.api.nvim_get_current_tabpage()
a.wrap(oil.select, 2)({ tab = true }) test_util.await(oil.select, 2, { tab = true })
assert.equals(2, #vim.api.nvim_list_tabpages()) assert.equals(2, #vim.api.nvim_list_tabpages())
assert.equals(1, #vim.api.nvim_tabpage_list_wins(0)) assert.equals(1, #vim.api.nvim_tabpage_list_wins(0))
assert.not_equals(tabpage, vim.api.nvim_get_current_tabpage()) assert.not_equals(tabpage, vim.api.nvim_get_current_tabpage())
end) end)
a.it('opens file in new split', function() it('opens file in new split', function()
test_util.oil_open() test_util.oil_open()
local winid = vim.api.nvim_get_current_win() local winid = vim.api.nvim_get_current_win()
a.wrap(oil.select, 2)({ vertical = true }) test_util.await(oil.select, 2, { vertical = true })
assert.equals(1, #vim.api.nvim_list_tabpages()) assert.equals(1, #vim.api.nvim_list_tabpages())
assert.equals(2, #vim.api.nvim_tabpage_list_wins(0)) assert.equals(2, #vim.api.nvim_tabpage_list_wins(0))
assert.not_equals(winid, vim.api.nvim_get_current_win()) assert.not_equals(winid, vim.api.nvim_get_current_win())
end) end)
a.it('opens multiple files in new tabs', function() it('opens multiple files in new tabs', function()
test_util.oil_open() test_util.oil_open()
vim.api.nvim_feedkeys('Vj', 'x', true) vim.api.nvim_feedkeys('Vj', 'x', true)
local tabpage = vim.api.nvim_get_current_tabpage() local tabpage = vim.api.nvim_get_current_tabpage()
a.wrap(oil.select, 2)({ tab = true }) test_util.await(oil.select, 2, { tab = true })
assert.equals(3, #vim.api.nvim_list_tabpages()) assert.equals(3, #vim.api.nvim_list_tabpages())
assert.equals(1, #vim.api.nvim_tabpage_list_wins(0)) assert.equals(1, #vim.api.nvim_tabpage_list_wins(0))
assert.not_equals(tabpage, vim.api.nvim_get_current_tabpage()) assert.not_equals(tabpage, vim.api.nvim_get_current_tabpage())
end) end)
a.it('opens multiple files in new splits', function() it('opens multiple files in new splits', function()
test_util.oil_open() test_util.oil_open()
vim.api.nvim_feedkeys('Vj', 'x', true) vim.api.nvim_feedkeys('Vj', 'x', true)
local winid = vim.api.nvim_get_current_win() local winid = vim.api.nvim_get_current_win()
a.wrap(oil.select, 2)({ vertical = true }) test_util.await(oil.select, 2, { vertical = true })
assert.equals(1, #vim.api.nvim_list_tabpages()) assert.equals(1, #vim.api.nvim_list_tabpages())
assert.equals(3, #vim.api.nvim_tabpage_list_wins(0)) assert.equals(3, #vim.api.nvim_tabpage_list_wins(0))
assert.not_equals(winid, vim.api.nvim_get_current_win()) assert.not_equals(winid, vim.api.nvim_get_current_win())
end) end)
a.describe('close after open', function() describe('close after open', function()
a.it('same window', function() it('same window', function()
vim.cmd.edit({ args = { 'foo' } }) vim.cmd.edit({ args = { 'foo' } })
local bufnr = vim.api.nvim_get_current_buf() local bufnr = vim.api.nvim_get_current_buf()
test_util.oil_open() test_util.oil_open()
-- Go to the bottom, so the cursor is not on a directory
vim.cmd.normal({ args = { 'G' } }) vim.cmd.normal({ args = { 'G' } })
a.wrap(oil.select, 2)({ close = true }) test_util.await(oil.select, 2, { close = true })
assert.equals(1, #vim.api.nvim_tabpage_list_wins(0)) assert.equals(1, #vim.api.nvim_tabpage_list_wins(0))
-- This one we actually don't expect the buffer to be the same as the initial buffer, because
-- we opened a file
assert.not_equals(bufnr, vim.api.nvim_get_current_buf()) assert.not_equals(bufnr, vim.api.nvim_get_current_buf())
assert.not_equals('oil', vim.bo.filetype) assert.not_equals('oil', vim.bo.filetype)
end) end)
a.it('split', function() it('split', function()
vim.cmd.edit({ args = { 'foo' } }) vim.cmd.edit({ args = { 'foo' } })
local bufnr = vim.api.nvim_get_current_buf() local bufnr = vim.api.nvim_get_current_buf()
local winid = vim.api.nvim_get_current_win() local winid = vim.api.nvim_get_current_win()
test_util.oil_open() test_util.oil_open()
a.wrap(oil.select, 2)({ vertical = true, close = true }) test_util.await(oil.select, 2, { vertical = true, close = true })
assert.equals(2, #vim.api.nvim_tabpage_list_wins(0)) assert.equals(2, #vim.api.nvim_tabpage_list_wins(0))
assert.equals(bufnr, vim.api.nvim_win_get_buf(winid)) assert.equals(bufnr, vim.api.nvim_win_get_buf(winid))
end) end)
a.it('tab', function() it('tab', function()
vim.cmd.edit({ args = { 'foo' } }) vim.cmd.edit({ args = { 'foo' } })
local bufnr = vim.api.nvim_get_current_buf() local bufnr = vim.api.nvim_get_current_buf()
local tabpage = vim.api.nvim_get_current_tabpage() local tabpage = vim.api.nvim_get_current_tabpage()
test_util.oil_open() test_util.oil_open()
a.wrap(oil.select, 2)({ tab = true, close = true }) test_util.await(oil.select, 2, { tab = true, close = true })
assert.equals(1, #vim.api.nvim_tabpage_list_wins(0)) assert.equals(1, #vim.api.nvim_tabpage_list_wins(0))
assert.equals(2, #vim.api.nvim_list_tabpages()) assert.equals(2, #vim.api.nvim_list_tabpages())
vim.api.nvim_set_current_tabpage(tabpage) vim.api.nvim_set_current_tabpage(tabpage)

View file

@ -1,4 +1,3 @@
require('plenary.async').tests.add_to_env()
local cache = require('oil.cache') local cache = require('oil.cache')
local test_adapter = require('oil.adapters.test') local test_adapter = require('oil.adapters.test')
local util = require('oil.util') local util = require('oil.util')
@ -34,15 +33,36 @@ local function throwiferr(err, ...)
end end
end end
M.oil_open = function(...)
a.wrap(require('oil').open, 3)(...)
end
M.await = function(fn, nargs, ...) M.await = function(fn, nargs, ...)
return throwiferr(a.wrap(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 end
M.wait_for_autocmd = a.wrap(function(autocmd, cb) 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 = { local opts = {
pattern = '*', pattern = '*',
nested = true, nested = true,
@ -53,30 +73,47 @@ M.wait_for_autocmd = a.wrap(function(autocmd, cb)
autocmd = autocmd[1] autocmd = autocmd[1]
opts[1] = nil opts[1] = nil
end end
opts.callback = vim.schedule_wrap(cb) opts.callback = vim.schedule_wrap(function()
triggered = true
end)
vim.api.nvim_create_autocmd(autocmd, opts) vim.api.nvim_create_autocmd(autocmd, opts)
end, 2) 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 = a.wrap(function(cb) M.wait_oil_ready = function()
util.run_after_load(0, vim.schedule_wrap(cb)) local ready = false
end, 1) 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 actions string[]
---@param timestep integer ---@param timestep integer
M.feedkeys = function(actions, timestep) M.feedkeys = function(actions, timestep)
timestep = timestep or 10 timestep = timestep or 10
a.util.sleep(timestep) vim.wait(timestep)
for _, action in ipairs(actions) do for _, action in ipairs(actions) do
a.util.sleep(timestep) vim.wait(timestep)
local escaped = vim.api.nvim_replace_termcodes(action, true, false, true) local escaped = vim.api.nvim_replace_termcodes(action, true, false, true)
vim.api.nvim_feedkeys(escaped, 'm', true) vim.api.nvim_feedkeys(escaped, 'm', true)
end end
a.util.sleep(timestep) vim.wait(timestep)
-- process pending keys until the queue is empty.
-- Note that this will exit insert mode.
vim.api.nvim_feedkeys('', 'x', true) vim.api.nvim_feedkeys('', 'x', true)
a.util.sleep(timestep) vim.wait(timestep)
end end
M.actions = { M.actions = {
@ -85,7 +122,6 @@ M.actions = {
open = function(args) open = function(args)
vim.schedule(function() vim.schedule(function()
vim.cmd.Oil({ args = args }) vim.cmd.Oil({ args = args })
-- If this buffer was already open, manually dispatch the autocmd to finish the wait
if vim.b.oil_ready then if vim.b.oil_ready then
vim.api.nvim_exec_autocmds('User', { vim.api.nvim_exec_autocmds('User', {
pattern = 'OilEnter', pattern = 'OilEnter',

View file

@ -1,25 +1,18 @@
local fs = require('oil.fs') local fs = require('oil.fs')
local test_util = require('tests.test_util') local test_util = require('spec.test_util')
local await = test_util.await
---@param path string ---@param path string
---@param cb fun(err: nil|string) local function touch(path)
local function touch(path, cb) local fd, open_err = vim.loop.fs_open(path, 'w', 420) -- 0644
vim.loop.fs_open(path, 'w', 420, function(err, fd) -- 0644 if not fd then
if err then error(open_err)
cb(err) end
else local shortpath = path:gsub('^[^' .. fs.sep .. ']*' .. fs.sep, '')
local shortpath = path:gsub('^[^' .. fs.sep .. ']*' .. fs.sep, '') local _, write_err = vim.loop.fs_write(fd, shortpath)
vim.loop.fs_write(fd, shortpath, nil, function(err2) if write_err then
if err2 then error(write_err)
cb(err2) end
else vim.loop.fs_close(fd)
vim.loop.fs_close(fd, cb)
end
end)
end
end)
end end
---@param filepath string ---@param filepath string
@ -28,11 +21,14 @@ local function exists(filepath)
local stat = vim.loop.fs_stat(filepath) local stat = vim.loop.fs_stat(filepath)
return stat ~= nil and stat.type ~= nil return stat ~= nil and stat.type ~= nil
end end
local TmpDir = {} local TmpDir = {}
TmpDir.new = function() TmpDir.new = function()
local path = await(vim.loop.fs_mkdtemp, 2, 'oil_test_XXXXXXXXX') local path, err = vim.loop.fs_mkdtemp('oil_test_XXXXXXXXX')
a.util.scheduler() if not path then
error(err)
end
return setmetatable({ path = path }, { return setmetatable({ path = path }, {
__index = TmpDir, __index = TmpDir,
}) })
@ -46,13 +42,12 @@ function TmpDir:create(paths)
for i, piece in ipairs(pieces) do for i, piece in ipairs(pieces) do
partial_path = fs.join(partial_path, piece) partial_path = fs.join(partial_path, piece)
if i == #pieces and not vim.endswith(partial_path, fs.sep) then if i == #pieces and not vim.endswith(partial_path, fs.sep) then
await(touch, 2, partial_path) touch(partial_path)
elseif not exists(partial_path) then elseif not exists(partial_path) then
vim.loop.fs_mkdir(partial_path, 493) vim.loop.fs_mkdir(partial_path, 493)
end end
end end
end end
a.util.scheduler()
end end
---@param filepath string ---@param filepath string
@ -65,12 +60,10 @@ local read_file = function(filepath)
local stat = vim.loop.fs_fstat(fd) local stat = vim.loop.fs_fstat(fd)
local content = vim.loop.fs_read(fd, stat.size) local content = vim.loop.fs_read(fd, stat.size)
vim.loop.fs_close(fd) vim.loop.fs_close(fd)
a.util.scheduler()
return content return content
end end
---@param dir string ---@param dir string
---@param cb fun(err: nil|string, entry: {type: oil.EntryType, name: string, root: string}
local function walk(dir) local function walk(dir)
local ret = {} local ret = {}
for name, type in vim.fs.dir(dir) do for name, type in vim.fs.dir(dir) do
@ -112,12 +105,11 @@ local assert_fs = function(root, paths)
end end
local expected_content = paths[shortpath] local expected_content = paths[shortpath]
paths[shortpath] = nil paths[shortpath] = nil
assert.truthy(expected_content, string.format("Unexpected entry '%s'", shortpath)) assert(expected_content, string.format("Unexpected entry '%s'", shortpath))
if entry.type == 'file' then if entry.type == 'file' then
local data = read_file(fullpath) local data = read_file(fullpath)
assert.equals( assert(
expected_content, expected_content == data,
data,
string.format( string.format(
"File '%s' expected content '%s' received '%s'", "File '%s' expected content '%s' received '%s'",
shortpath, shortpath,
@ -129,8 +121,8 @@ local assert_fs = function(root, paths)
end end
for k, v in pairs(paths) do for k, v in pairs(paths) do
assert.falsy( assert(
k, not k,
string.format( string.format(
"Expected %s '%s', but it was not found", "Expected %s '%s', but it was not found",
v == true and 'directory' or 'file', v == true and 'directory' or 'file',
@ -142,27 +134,23 @@ end
---@param paths table<string, string> ---@param paths table<string, string>
function TmpDir:assert_fs(paths) function TmpDir:assert_fs(paths)
a.util.scheduler()
assert_fs(self.path, paths) assert_fs(self.path, paths)
end end
function TmpDir:assert_exists(path) function TmpDir:assert_exists(path)
a.util.scheduler()
path = fs.join(self.path, path) path = fs.join(self.path, path)
local stat = vim.loop.fs_stat(path) local stat = vim.loop.fs_stat(path)
assert.truthy(stat, string.format("Expected path '%s' to exist", path)) assert(stat, string.format("Expected path '%s' to exist", path))
end end
function TmpDir:assert_not_exists(path) function TmpDir:assert_not_exists(path)
a.util.scheduler()
path = fs.join(self.path, path) path = fs.join(self.path, path)
local stat = vim.loop.fs_stat(path) local stat = vim.loop.fs_stat(path)
assert.falsy(stat, string.format("Expected path '%s' to not exist", path)) assert(not stat, string.format("Expected path '%s' to not exist", path))
end end
function TmpDir:dispose() function TmpDir:dispose()
await(fs.recursive_delete, 3, 'directory', self.path) test_util.await_throwiferr(fs.recursive_delete, 3, 'directory', self.path)
a.util.scheduler()
end end
return TmpDir return TmpDir

View file

@ -1,19 +1,18 @@
require('plenary.async').tests.add_to_env() local TmpDir = require('spec.tmpdir')
local TmpDir = require('tests.tmpdir') local test_util = require('spec.test_util')
local test_util = require('tests.test_util')
a.describe('freedesktop', function() describe('freedesktop', function()
local tmpdir local tmpdir
local tmphome local tmphome
local home = vim.env.XDG_DATA_HOME local home = vim.env.XDG_DATA_HOME
a.before_each(function() before_each(function()
require('oil.config').delete_to_trash = true require('oil.config').delete_to_trash = true
tmpdir = TmpDir.new() tmpdir = TmpDir.new()
tmphome = TmpDir.new() tmphome = TmpDir.new()
package.loaded['oil.adapters.trash'] = require('oil.adapters.trash.freedesktop') package.loaded['oil.adapters.trash'] = require('oil.adapters.trash.freedesktop')
vim.env.XDG_DATA_HOME = tmphome.path vim.env.XDG_DATA_HOME = tmphome.path
end) end)
a.after_each(function() after_each(function()
vim.env.XDG_DATA_HOME = home vim.env.XDG_DATA_HOME = home
if tmpdir then if tmpdir then
tmpdir:dispose() tmpdir:dispose()
@ -25,7 +24,7 @@ a.describe('freedesktop', function()
package.loaded['oil.adapters.trash'] = nil package.loaded['oil.adapters.trash'] = nil
end) end)
a.it('files can be moved to the trash', function() it('files can be moved to the trash', function()
tmpdir:create({ 'a.txt', 'foo/b.txt' }) tmpdir:create({ 'a.txt', 'foo/b.txt' })
test_util.actions.open({ tmpdir.path }) test_util.actions.open({ tmpdir.path })
test_util.actions.focus('a.txt') test_util.actions.focus('a.txt')
@ -39,7 +38,7 @@ a.describe('freedesktop', function()
assert.are.same({ 'a.txt' }, test_util.parse_entries(0)) assert.are.same({ 'a.txt' }, test_util.parse_entries(0))
end) end)
a.it('deleting a file moves it to trash', function() it('deleting a file moves it to trash', function()
tmpdir:create({ 'a.txt', 'foo/b.txt' }) tmpdir:create({ 'a.txt', 'foo/b.txt' })
test_util.actions.open({ tmpdir.path }) test_util.actions.open({ tmpdir.path })
test_util.actions.focus('a.txt') test_util.actions.focus('a.txt')
@ -51,7 +50,7 @@ a.describe('freedesktop', function()
assert.are.same({ 'a.txt' }, test_util.parse_entries(0)) assert.are.same({ 'a.txt' }, test_util.parse_entries(0))
end) end)
a.it('deleting a directory moves it to trash', function() it('deleting a directory moves it to trash', function()
tmpdir:create({ 'a.txt', 'foo/b.txt' }) tmpdir:create({ 'a.txt', 'foo/b.txt' })
test_util.actions.open({ tmpdir.path }) test_util.actions.open({ tmpdir.path })
test_util.actions.focus('foo/') test_util.actions.focus('foo/')
@ -63,7 +62,7 @@ a.describe('freedesktop', function()
assert.are.same({ 'foo/' }, test_util.parse_entries(0)) assert.are.same({ 'foo/' }, test_util.parse_entries(0))
end) end)
a.it('deleting a file from trash deletes it permanently', function() it('deleting a file from trash deletes it permanently', function()
tmpdir:create({ 'a.txt' }) tmpdir:create({ 'a.txt' })
test_util.actions.open({ tmpdir.path }) test_util.actions.open({ tmpdir.path })
test_util.actions.focus('a.txt') test_util.actions.focus('a.txt')
@ -78,7 +77,7 @@ a.describe('freedesktop', function()
assert.are.same({}, test_util.parse_entries(0)) assert.are.same({}, test_util.parse_entries(0))
end) end)
a.it('cannot create files in the trash', function() it('cannot create files in the trash', function()
tmpdir:create({ 'a.txt' }) tmpdir:create({ 'a.txt' })
test_util.actions.open({ tmpdir.path }) test_util.actions.open({ tmpdir.path })
test_util.actions.focus('a.txt') test_util.actions.focus('a.txt')
@ -91,7 +90,7 @@ a.describe('freedesktop', function()
assert.are.same({ 'a.txt' }, test_util.parse_entries(0)) assert.are.same({ 'a.txt' }, test_util.parse_entries(0))
end) end)
a.it('cannot rename files in the trash', function() it('cannot rename files in the trash', function()
tmpdir:create({ 'a.txt' }) tmpdir:create({ 'a.txt' })
test_util.actions.open({ tmpdir.path }) test_util.actions.open({ tmpdir.path })
test_util.actions.focus('a.txt') test_util.actions.focus('a.txt')
@ -104,7 +103,7 @@ a.describe('freedesktop', function()
assert.are.same({ 'a.txt' }, test_util.parse_entries(0)) assert.are.same({ 'a.txt' }, test_util.parse_entries(0))
end) end)
a.it('cannot copy files in the trash', function() it('cannot copy files in the trash', function()
tmpdir:create({ 'a.txt' }) tmpdir:create({ 'a.txt' })
test_util.actions.open({ tmpdir.path }) test_util.actions.open({ tmpdir.path })
test_util.actions.focus('a.txt') test_util.actions.focus('a.txt')
@ -117,7 +116,7 @@ a.describe('freedesktop', function()
assert.are.same({ 'a.txt' }, test_util.parse_entries(0)) assert.are.same({ 'a.txt' }, test_util.parse_entries(0))
end) end)
a.it('can restore files from trash', function() it('can restore files from trash', function()
tmpdir:create({ 'a.txt' }) tmpdir:create({ 'a.txt' })
test_util.actions.open({ tmpdir.path }) test_util.actions.open({ tmpdir.path })
test_util.actions.focus('a.txt') test_util.actions.focus('a.txt')
@ -135,7 +134,7 @@ a.describe('freedesktop', function()
}) })
end) end)
a.it('can have multiple files with the same name in trash', function() it('can have multiple files with the same name in trash', function()
tmpdir:create({ 'a.txt' }) tmpdir:create({ 'a.txt' })
test_util.actions.open({ tmpdir.path }) test_util.actions.open({ tmpdir.path })
vim.api.nvim_feedkeys('dd', 'x', true) vim.api.nvim_feedkeys('dd', 'x', true)

View file

@ -1,13 +1,12 @@
require('plenary.async').tests.add_to_env()
local oil = require('oil') local oil = require('oil')
local test_util = require('tests.test_util') local test_util = require('spec.test_util')
a.describe('window options', function() describe('window options', function()
after_each(function() after_each(function()
test_util.reset_editor() test_util.reset_editor()
end) end)
a.it('Restores window options on close', function() it('Restores window options on close', function()
vim.cmd.edit({ args = { 'README.md' } }) vim.cmd.edit({ args = { 'README.md' } })
test_util.oil_open() test_util.oil_open()
assert.equals('no', vim.o.signcolumn) assert.equals('no', vim.o.signcolumn)
@ -15,21 +14,21 @@ a.describe('window options', function()
assert.equals('auto', vim.o.signcolumn) assert.equals('auto', vim.o.signcolumn)
end) end)
a.it('Restores window options on edit', function() it('Restores window options on edit', function()
test_util.oil_open() test_util.oil_open()
assert.equals('no', vim.o.signcolumn) assert.equals('no', vim.o.signcolumn)
vim.cmd.edit({ args = { 'README.md' } }) vim.cmd.edit({ args = { 'README.md' } })
assert.equals('auto', vim.o.signcolumn) assert.equals('auto', vim.o.signcolumn)
end) end)
a.it('Restores window options on split <filename>', function() it('Restores window options on split <filename>', function()
test_util.oil_open() test_util.oil_open()
assert.equals('no', vim.o.signcolumn) assert.equals('no', vim.o.signcolumn)
vim.cmd.split({ args = { 'README.md' } }) vim.cmd.split({ args = { 'README.md' } })
assert.equals('auto', vim.o.signcolumn) assert.equals('auto', vim.o.signcolumn)
end) end)
a.it('Restores window options on split', function() it('Restores window options on split', function()
test_util.oil_open() test_util.oil_open()
assert.equals('no', vim.o.signcolumn) assert.equals('no', vim.o.signcolumn)
vim.cmd.split() vim.cmd.split()
@ -37,14 +36,14 @@ a.describe('window options', function()
assert.equals('auto', vim.o.signcolumn) assert.equals('auto', vim.o.signcolumn)
end) end)
a.it('Restores window options on tabnew <filename>', function() it('Restores window options on tabnew <filename>', function()
test_util.oil_open() test_util.oil_open()
assert.equals('no', vim.o.signcolumn) assert.equals('no', vim.o.signcolumn)
vim.cmd.tabnew({ args = { 'README.md' } }) vim.cmd.tabnew({ args = { 'README.md' } })
assert.equals('auto', vim.o.signcolumn) assert.equals('auto', vim.o.signcolumn)
end) end)
a.it('Restores window options on tabnew', function() it('Restores window options on tabnew', function()
test_util.oil_open() test_util.oil_open()
assert.equals('no', vim.o.signcolumn) assert.equals('no', vim.o.signcolumn)
vim.cmd.tabnew() vim.cmd.tabnew()
@ -52,7 +51,7 @@ a.describe('window options', function()
assert.equals('auto', vim.o.signcolumn) assert.equals('auto', vim.o.signcolumn)
end) end)
a.it('Sets the window options when re-entering oil buffer', function() it('Sets the window options when re-entering oil buffer', function()
oil.open() oil.open()
test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' }) test_util.wait_for_autocmd({ 'User', pattern = 'OilEnter' })
assert.truthy(vim.w.oil_did_enter) assert.truthy(vim.w.oil_did_enter)

View file

@ -1,5 +0,0 @@
vim.opt.runtimepath:append('.')
vim.o.swapfile = false
vim.bo.swapfile = false
require('tests.test_util').reset_editor()