diff --git a/Makefile b/Makefile index 3c510f2..57a5e49 100644 --- a/Makefile +++ b/Makefile @@ -6,8 +6,10 @@ BARE_VERSION = $(patsubst v%,%,$(VERSION)) DIST = dist ARCHIVE = $(DIST)/arti-pack-$(BARE_VERSION).zip +# --clean keeps the user's config and site packages off the runtimepath, so an +# installed arti-pack cannot stand in for the working tree. test: - nvim --headless -l tests/run.lua + nvim --clean --headless -l tests/run.lua lint: luacheck lua tests diff --git a/tests/hermetic.lua b/tests/hermetic.lua new file mode 100644 index 0000000..4845197 --- /dev/null +++ b/tests/hermetic.lua @@ -0,0 +1,26 @@ +local M = { root = nil, sources = {} } + +local uv = vim.uv or vim.loop + +-- Neovim inserts its runtimepath searcher ahead of the package.path one, so an +-- installed copy of arti-pack resolves before the working tree. Claim the first +-- slot for the repository instead. +function M.install(root) + M.root = root + table.insert(package.loaders, 1, function(name) + local rel = name:gsub('%.', '/') + for _, candidate in ipairs({ + root .. '/lua/' .. rel .. '.lua', + root .. '/lua/' .. rel .. '/init.lua', + root .. '/' .. rel .. '.lua', + }) do + if uv.fs_stat(candidate) then + M.sources[name] = candidate + return assert(loadfile(candidate)) + end + end + return nil + end) +end + +return M diff --git a/tests/hermetic_spec.lua b/tests/hermetic_spec.lua new file mode 100644 index 0000000..772be10 --- /dev/null +++ b/tests/hermetic_spec.lua @@ -0,0 +1,49 @@ +local t = require('tests.harness') +local hermetic = require('tests.hermetic') + +local MODULES = { + 'arti-pack', + 'arti-pack.spec', + 'arti-pack.fs', + 'arti-pack.archive', + 'arti-pack.lock', + 'arti-pack.installer', +} + +local function shadow_plugin() + local dir = vim.fn.tempname() + vim.fn.mkdir(dir .. '/lua/arti-pack', 'p') + vim.fn.writefile({ 'return { shadowed = true }' }, dir .. '/lua/arti-pack/spec.lua') + vim.fn.writefile({ 'return { shadowed = true }' }, dir .. '/lua/arti-pack/init.lua') + return dir +end + +t.describe('test harness', function() + t.it('loads every module from the repository', function() + for _, name in ipairs(MODULES) do + require(name) + local source = hermetic.sources[name] + t.truthy(source, name .. ' was not resolved by the repository loader') + t.eq(hermetic.root, source:sub(1, #hermetic.root), name .. ' resolved outside the repository') + end + end) + + t.it('outranks an arti-pack installed on the runtimepath', function() + local dir = shadow_plugin() + vim.opt.runtimepath:prepend(dir) + + local ok, err = pcall(function() + t.eq(dir .. '/lua/arti-pack/spec.lua', vim.api.nvim_get_runtime_file('lua/arti-pack/spec.lua', false)[1]) + + for _, name in ipairs({ 'arti-pack', 'arti-pack.spec' }) do + package.loaded[name] = nil + t.falsy(require(name).shadowed, name .. ' came from the runtimepath copy') + t.eq(hermetic.root, hermetic.sources[name]:sub(1, #hermetic.root)) + end + end) + + vim.opt.runtimepath:remove(dir) + vim.fn.delete(dir, 'rf') + assert(ok, err) + end) +end) diff --git a/tests/run.lua b/tests/run.lua index 49d18f2..76741df 100644 --- a/tests/run.lua +++ b/tests/run.lua @@ -1,14 +1,20 @@ local root = vim.fn.fnamemodify(debug.getinfo(1, 'S').source:sub(2), ':p:h:h') -package.path = table.concat({ - root .. '/lua/?.lua', - root .. '/lua/?/init.lua', - root .. '/?.lua', - package.path, -}, ';') + +local hermetic = dofile(root .. '/tests/hermetic.lua') +package.loaded['tests.hermetic'] = hermetic +hermetic.install(root) local t = require('tests.harness') -for _, name in ipairs({ 'spec_spec', 'fs_spec', 'archive_spec', 'lock_spec', 'installer_spec', 'init_spec' }) do +for _, name in ipairs({ + 'hermetic_spec', + 'spec_spec', + 'fs_spec', + 'archive_spec', + 'lock_spec', + 'installer_spec', + 'init_spec', +}) do require('tests.' .. name) end