async vim.loop calls with coroutines block tests

Open
#542 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
48/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Stale
Tech stack
lua
Domain
testing-qa

Research direction

Start by running make test with the reproduction in test/test_spec.lua and test/minimal_init.lua. Trace how plenary.test_harness handles the coroutine yield and vim.loop.fs_stat callback, using the Makefile command as the entry point. Done means the reproduced test exits successfully instead of hanging, while synchronous vim.loop calls remain working.

Written by the indexing model from the issue text.

Description

Using async vim.loop functions (not the Plenary async ones) block Plenary's test harness.

Reproduction

Have the following files in a directory:

-- test/test_spec.lua
describe("test", function()
	it("runs the test", function()
		local co = coroutine.running()
		assert(co, "not running inside a coroutine")

		local is_done = false
		vim.loop.fs_stat("/bin", function(err, stats)
			is_done = true
			coroutine.resume(co)
		end)
		if not is_done then
			coroutine.yield()
		end
		assert.equals(true, true)
	end)
end)
-- test/minimal_init.lua
local M = {}

function M.root(root)
    local f = debug.getinfo(1, "S").source:sub(2)
    return vim.fn.fnamemodify(f, ":p:h:h") .. "/" .. (root or "")
end

---@param plugin string
function M.load(plugin)
    local name = plugin:match(".*/(.*)")
    local package_root = M.root(".tests/site/pack/deps/start/")
    if not vim.loop.fs_stat(package_root .. name) then
        print("Installing " .. plugin)
        vim.fn.mkdir(package_root, "p")
        vim.fn.system({
            "git",
            "clone",
            "--depth=1",
            "https://github.com/" .. plugin .. ".git",
            package_root .. "/" .. name,
        })
    end
end

function M.setup()
    vim.cmd([[set runtimepath=$VIMRUNTIME]])
    vim.opt.runtimepath:append(M.root())
    vim.opt.packpath = { M.root(".tests/site") }
    M.load("nvim-lua/plenary.nvim")
    vim.env.XDG_CONFIG_HOME = M.root(".tests/config")
    vim.env.XDG_DATA_HOME = M.root(".tests/data")
    vim.env.XDG_STATE_HOME = M.root(".tests/state")
    vim.env.XDG_CACHE_HOME = M.root(".tests/cache")
end

vim.o.swapfile = false
_G.__TEST = true

M.setup()
# Makefile
.PHONY: test

test:
	nvim --headless --noplugin -u test/minimal_init.lua -c "lua require(\"plenary.test_harness\").test_directory_command('test {minimal_init = \"test/minimal_init.lua\"}')"

Run make test. This will get printed:

nvim --headless --noplugin -u test/minimal_init.lua -c "lua require(\"plenary.test_harness\").test_directory_command('test {minimal_init = \"test/minimal_init.lua\"}')"
Starting...Scheduling: test/client_spec.lua

========================================	
Testing: 	/Users/grzesiek/Code/plenary-busted-loop-async-bug/test/client_spec.lua
Success	||	test runs the test	
	
Success: 	1	
Failed : 	0	
Errors : 	0	
========================================	

and the run will hang and never finish.

Expected behavior

make test finishes successfully.

Notes

There's an issue on testing async code, https://github.com/nvim-lua/plenary.nvim/issues/424. The solution provided there works with code that uses vim.defer_fn or vim.schedule. It doesn't work with vim.loop functions.

Synchronous vim.loop functions work just fine.

Dominant language
Lua
Stars
3.5k
Forks
340
PR merge metrics
No merged PRs in 30d

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from nvim-lua/plenary.nvim

All issues in nvim-lua/plenary.nvim

Similar issues

More Lua issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.