From 322b2731ed68da322059f634cacf4234ce141332 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 8 Feb 2020 15:29:47 -0800 Subject: [PATCH 1/7] deps: lua-client 0.2.2-1 --- third-party/cmake/BuildLuarocks.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/third-party/cmake/BuildLuarocks.cmake b/third-party/cmake/BuildLuarocks.cmake index 4b1b94a46b..c5595bf840 100644 --- a/third-party/cmake/BuildLuarocks.cmake +++ b/third-party/cmake/BuildLuarocks.cmake @@ -217,10 +217,10 @@ if(USE_BUNDLED_BUSTED) endif() add_custom_target(luv DEPENDS ${ROCKS_DIR}/luv) - # nvim-client + # nvim-client: https://github.com/neovim/lua-client add_custom_command(OUTPUT ${ROCKS_DIR}/nvim-client COMMAND ${LUAROCKS_BINARY} - ARGS build nvim-client 0.2.0-1 ${LUAROCKS_BUILDARGS} + ARGS build nvim-client 0.2.2-1 ${LUAROCKS_BUILDARGS} DEPENDS luv) add_custom_target(nvim-client DEPENDS ${ROCKS_DIR}/nvim-client) From b04165859d0c11e2bdcacd50efb0ec6c2b2a6c0c Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 8 Feb 2020 15:35:28 -0800 Subject: [PATCH 2/7] test: style --- test/functional/plugin/lsp_spec.lua | 62 ++++++++++++++++++----------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 4829a33861..1db9bf306d 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -16,18 +16,18 @@ local run, stop = helpers.run, helpers.stop if helpers.pending_win32(pending) then return end -local lsp_test_rpc_server_file = "test/functional/fixtures/lsp-test-rpc-server.lua" +local fake_lsp_server_file = "test/functional/fixtures/lsp-test-rpc-server.lua" if iswin() then - lsp_test_rpc_server_file = lsp_test_rpc_server_file:gsub("/", "\\") + fake_lsp_server_file = fake_lsp_server_file:gsub("/", "\\") end -local function test_rpc_server_setup(test_name, timeout_ms) +local function fake_lsp_server_setup(test_name, timeout_ms) exec_lua([=[ lsp = require('vim.lsp') local test_name, fixture_filename, timeout = ... TEST_RPC_CLIENT_ID = lsp.start_client { cmd = { - vim.api.nvim_get_vvar("progpath"), '-Es', '-u', 'NONE', '--headless', + vim.v.progpath, '-Es', '-u', 'NONE', '--headless', "-c", string.format("lua TEST_NAME = %q", test_name), "-c", string.format("lua TIMEOUT = %d", timeout), "-c", "luafile "..fixture_filename, @@ -48,13 +48,13 @@ local function test_rpc_server_setup(test_name, timeout_ms) vim.rpcnotify(1, "exit", ...) end; } - ]=], test_name, lsp_test_rpc_server_file, timeout_ms or 1e3) + ]=], test_name, fake_lsp_server_file, timeout_ms or 1e3) end local function test_rpc_server(config) if config.test_name then clear() - test_rpc_server_setup(config.test_name, config.timeout_ms or 1e3) + fake_lsp_server_setup(config.test_name, config.timeout_ms or 1e3) end local client = setmetatable({}, { __index = function(_, name) @@ -118,7 +118,7 @@ describe('LSP', function() function test__start_client() return lsp.start_client { cmd = { - vim.api.nvim_get_vvar("progpath"), '-Es', '-u', 'NONE', '--headless', + vim.v.progpath, '-Es', '-u', 'NONE', '--headless', "-c", string.format("lua TEST_NAME = %q", test_name), "-c", "luafile "..fixture_filename; }; @@ -126,7 +126,7 @@ describe('LSP', function() } end TEST_CLIENT1 = test__start_client() - ]=], test_name, lsp_test_rpc_server_file) + ]=], test_name, fake_lsp_server_file) end) after_each(function() @@ -195,7 +195,8 @@ describe('LSP', function() end; -- If the program timed out, then code will be nil. on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; -- Note that NIL must be used here. -- on_callback(err, method, result, client_id) @@ -216,7 +217,8 @@ describe('LSP', function() client.stop() end; on_exit = function(code, signal) - eq(1, code, "exit code") eq(0, signal, "exit signal") + eq(1, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(...) eq(table.remove(expected_callbacks), {...}, "expected callback") @@ -237,7 +239,8 @@ describe('LSP', function() client.notify('exit') end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(...) eq(table.remove(expected_callbacks), {...}, "expected callback") @@ -255,7 +258,8 @@ describe('LSP', function() client.stop() end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(...) eq(table.remove(expected_callbacks), {...}, "expected callback") @@ -294,7 +298,8 @@ describe('LSP', function() client.notify('finish') end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") @@ -336,7 +341,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -378,7 +384,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -420,7 +427,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -468,7 +476,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -516,7 +525,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -536,7 +546,7 @@ describe('LSP', function() end) -- TODO(askhan) we don't support full for now, so we can disable these tests. - pending('should check the body and didChange incremental normal mode editting', function() + pending('should check the body and didChange incremental normal mode editing', function() local expected_callbacks = { {NIL, "shutdown", {}, 1}; {NIL, "finish", {}, 1}; @@ -544,7 +554,7 @@ describe('LSP', function() } local client test_rpc_server { - test_name = "basic_check_buffer_open_and_change_incremental_editting"; + test_name = "basic_check_buffer_open_and_change_incremental_editing"; on_setup = function() exec_lua [[ BUFFER = vim.api.nvim_create_buf(false, true) @@ -564,7 +574,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -607,7 +618,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -657,7 +669,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -699,7 +712,8 @@ describe('LSP', function() client.stop(true) end; on_exit = function(code, signal) - eq(0, code, "exit code") eq(0, signal, "exit signal") + eq(0, code, "exit code") + eq(0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") From c15bd6cd279dbed5d246af05c4c0625387be02af Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 8 Feb 2020 17:16:43 -0800 Subject: [PATCH 3/7] test/LSP: use less-generic exit code - os.exit(1) is too generic, since code 1 may be caused by Nvim exiting for some other reason. Change it to os.exit(101). - style: de-architect json_encode/json_decode calls. Failure seen in travis macOS job: https://travis-ci.org/neovim/neovim/jobs/647849133 [ FAILED ] test/functional/plugin/lsp_spec.lua@ 266 SP basic_init test should not send didOpen if the buffer closes before init test/functional/plugin/lsp_spec.lua:297: exit code Expected objects to be the same. Passed in: (number) 1 Expected: (number) 0 stack traceback: test/functional/plugin/lsp_spec.lua:297: in function 'on_exit' test/functional/plugin/lsp_spec.lua:100: in function 'test_rpc_server' test/functional/plugin/lsp_spec.lua:272: in function --- .../fixtures/lsp-test-rpc-server.lua | 27 +++---------------- test/functional/plugin/lsp_spec.lua | 2 +- 2 files changed, 4 insertions(+), 25 deletions(-) diff --git a/test/functional/fixtures/lsp-test-rpc-server.lua b/test/functional/fixtures/lsp-test-rpc-server.lua index 798883ced0..44117bea30 100644 --- a/test/functional/fixtures/lsp-test-rpc-server.lua +++ b/test/functional/fixtures/lsp-test-rpc-server.lua @@ -1,24 +1,5 @@ local protocol = require 'vim.lsp.protocol' --- Internal utility methods. - --- TODO replace with a better implementation. -local function json_encode(data) - local status, result = pcall(vim.fn.json_encode, data) - if status then - return result - else - return nil, result - end -end -local function json_decode(data) - local status, result = pcall(vim.fn.json_decode, data) - if status then - return result - else - return nil, result - end -end local function message_parts(sep, ...) local parts = {} @@ -49,16 +30,14 @@ local function format_message_with_content_length(encoded_message) } end --- Server utility methods. - local function read_message() local line = io.read("*l") local length = line:lower():match("content%-length:%s*(%d+)") - return assert(json_decode(io.read(2 + length):sub(2)), "read_message.json_decode") + return vim.fn.json_decode(io.read(2 + length):sub(2)) end local function send(payload) - io.stdout:write(format_message_with_content_length(json_encode(payload))) + io.stdout:write(format_message_with_content_length(vim.fn.json_encode(payload))) end local function respond(id, err, result) @@ -454,6 +433,6 @@ kill_timer:stop() kill_timer:close() if not status then io.stderr:write(err) - os.exit(1) + os.exit(101) end os.exit(0) diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 1db9bf306d..cab1fb0d79 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -217,7 +217,7 @@ describe('LSP', function() client.stop() end; on_exit = function(code, signal) - eq(1, code, "exit code") + eq(101, code, "exit code") eq(0, signal, "exit signal") end; on_callback = function(...) From 1eb0f5371ae8cee90b97f586a99505cfa5913504 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 8 Feb 2020 17:25:53 -0800 Subject: [PATCH 4/7] LSP: fix validate_client_config - `cmd_env` is a table not a function. - tests: Set $NVIM_LOG_FILE for fake LSP server. --- runtime/lua/vim/lsp.lua | 2 +- ...{lsp-test-rpc-server.lua => fake-lsp-server.lua} | 0 test/functional/plugin/lsp_spec.lua | 13 ++++++++----- 3 files changed, 9 insertions(+), 6 deletions(-) rename test/functional/fixtures/{lsp-test-rpc-server.lua => fake-lsp-server.lua} (100%) diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index e5b6653346..94f0d62d8d 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -154,7 +154,7 @@ local function validate_client_config(config) callbacks = { config.callbacks, "t", true }; capabilities = { config.capabilities, "t", true }; cmd_cwd = { config.cmd_cwd, optional_validator(is_dir), "directory" }; - cmd_env = { config.cmd_env, "f", true }; + cmd_env = { config.cmd_env, "t", true }; name = { config.name, 's', true }; on_error = { config.on_error, "f", true }; on_exit = { config.on_exit, "f", true }; diff --git a/test/functional/fixtures/lsp-test-rpc-server.lua b/test/functional/fixtures/fake-lsp-server.lua similarity index 100% rename from test/functional/fixtures/lsp-test-rpc-server.lua rename to test/functional/fixtures/fake-lsp-server.lua diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index cab1fb0d79..c384fdedf3 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -16,16 +16,16 @@ local run, stop = helpers.run, helpers.stop if helpers.pending_win32(pending) then return end -local fake_lsp_server_file = "test/functional/fixtures/lsp-test-rpc-server.lua" -if iswin() then - fake_lsp_server_file = fake_lsp_server_file:gsub("/", "\\") -end +local fake_lsp_server_file = 'test/functional/fixtures/fake-lsp-server.lua' local function fake_lsp_server_setup(test_name, timeout_ms) exec_lua([=[ lsp = require('vim.lsp') local test_name, fixture_filename, timeout = ... TEST_RPC_CLIENT_ID = lsp.start_client { + cmd_env = { + NVIM_LOG_FILE = 'Xtest-fake-lsp-server.log' + }; cmd = { vim.v.progpath, '-Es', '-u', 'NONE', '--headless', "-c", string.format("lua TEST_NAME = %q", test_name), @@ -117,6 +117,9 @@ describe('LSP', function() local test_name, fixture_filename = ... function test__start_client() return lsp.start_client { + cmd_env = { + NVIM_LOG_FILE = 'Xtest-fake-lsp-server.log' + }; cmd = { vim.v.progpath, '-Es', '-u', 'NONE', '--headless', "-c", string.format("lua TEST_NAME = %q", test_name), @@ -217,7 +220,7 @@ describe('LSP', function() client.stop() end; on_exit = function(code, signal) - eq(101, code, "exit code") + eq(101, code, "exit code") -- See fake-lsp-server.lua eq(0, signal, "exit signal") end; on_callback = function(...) From 4cf48dc329e3e3f451ecf902af9af6317d44bb40 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 8 Feb 2020 18:08:02 -0800 Subject: [PATCH 5/7] test/LSP: dump logs on error This will help debug CI flakey failures. TODO: helpers.assert_log() -- Explicitly check contents of the logfile. --- test/functional/plugin/lsp_spec.lua | 79 ++++++++++++++++------------- test/helpers.lua | 14 +++-- 2 files changed, 54 insertions(+), 39 deletions(-) diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index c384fdedf3..b2d00a400a 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -5,8 +5,8 @@ local buf_lines = helpers.buf_lines local dedent = helpers.dedent local exec_lua = helpers.exec_lua local eq = helpers.eq +local eq_dumplog = helpers.eq_dumplog local insert = helpers.insert -local iswin = helpers.iswin local retry = helpers.retry local NIL = helpers.NIL @@ -14,17 +14,24 @@ local NIL = helpers.NIL -- yield. local run, stop = helpers.run, helpers.stop +-- TODO(justinmk): hangs on Windows https://github.com/neovim/neovim/pull/11837 if helpers.pending_win32(pending) then return end -local fake_lsp_server_file = 'test/functional/fixtures/fake-lsp-server.lua' +-- Fake LSP server. +local fake_lsp_code = 'test/functional/fixtures/fake-lsp-server.lua' +local fake_lsp_logfile = 'Xtest-fake-lsp.log' + +teardown(function() + os.remove(fake_lsp_logfile) +end) local function fake_lsp_server_setup(test_name, timeout_ms) exec_lua([=[ lsp = require('vim.lsp') - local test_name, fixture_filename, timeout = ... + local test_name, fixture_filename, logfile, timeout = ... TEST_RPC_CLIENT_ID = lsp.start_client { cmd_env = { - NVIM_LOG_FILE = 'Xtest-fake-lsp-server.log' + NVIM_LOG_FILE = logfile; }; cmd = { vim.v.progpath, '-Es', '-u', 'NONE', '--headless', @@ -48,7 +55,7 @@ local function fake_lsp_server_setup(test_name, timeout_ms) vim.rpcnotify(1, "exit", ...) end; } - ]=], test_name, fake_lsp_server_file, timeout_ms or 1e3) + ]=], test_name, fake_lsp_code, fake_lsp_logfile, timeout_ms or 1e3) end local function test_rpc_server(config) @@ -114,11 +121,11 @@ describe('LSP', function() local test_name = "basic_init" exec_lua([=[ lsp = require('vim.lsp') - local test_name, fixture_filename = ... + local test_name, fixture_filename, logfile = ... function test__start_client() return lsp.start_client { cmd_env = { - NVIM_LOG_FILE = 'Xtest-fake-lsp-server.log' + NVIM_LOG_FILE = logfile; }; cmd = { vim.v.progpath, '-Es', '-u', 'NONE', '--headless', @@ -129,7 +136,7 @@ describe('LSP', function() } end TEST_CLIENT1 = test__start_client() - ]=], test_name, fake_lsp_server_file) + ]=], test_name, fake_lsp_code, fake_lsp_logfile) end) after_each(function() @@ -198,8 +205,8 @@ describe('LSP', function() end; -- If the program timed out, then code will be nil. on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; -- Note that NIL must be used here. -- on_callback(err, method, result, client_id) @@ -220,8 +227,8 @@ describe('LSP', function() client.stop() end; on_exit = function(code, signal) - eq(101, code, "exit code") -- See fake-lsp-server.lua - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 101, code, "exit code") -- See fake-lsp-server.lua + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(...) eq(table.remove(expected_callbacks), {...}, "expected callback") @@ -242,8 +249,8 @@ describe('LSP', function() client.notify('exit') end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(...) eq(table.remove(expected_callbacks), {...}, "expected callback") @@ -261,8 +268,8 @@ describe('LSP', function() client.stop() end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(...) eq(table.remove(expected_callbacks), {...}, "expected callback") @@ -301,8 +308,8 @@ describe('LSP', function() client.notify('finish') end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") @@ -344,8 +351,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -387,8 +394,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -430,8 +437,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -479,8 +486,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -528,8 +535,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -577,8 +584,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -621,8 +628,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -672,8 +679,8 @@ describe('LSP', function() ]] end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) if method == 'start' then @@ -715,8 +722,8 @@ describe('LSP', function() client.stop(true) end; on_exit = function(code, signal) - eq(0, code, "exit code") - eq(0, signal, "exit signal") + eq_dumplog(fake_lsp_logfile, 0, code, "exit code") + eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") end; on_callback = function(err, method, params, client_id) eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") diff --git a/test/helpers.lua b/test/helpers.lua index 98f003f208..d9e1f4a963 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -58,6 +58,14 @@ local check_logs_useless_lines = { function module.eq(expected, actual, context) return assert.are.same(expected, actual, context) end +-- Like eq(), but includes tail of `logfile` in failure message. +function module.eq_dumplog(logfile, expected, actual, context) + local status, rv = pcall(module.eq, expected, actual, context) + if not status then + local logtail = module.read_nvim_log(logfile) + error(string.format('%s\n%s', rv, logtail)) + end +end function module.neq(expected, actual, context) return assert.are_not.same(expected, actual, context) end @@ -737,10 +745,10 @@ function module.isCI(name) end --- Gets the contents of $NVIM_LOG_FILE for printing to the build log. +-- Gets the contents of `logfile` for printing to the build log. -- Also moves the file to "${NVIM_LOG_FILE}.displayed" on CI environments. -function module.read_nvim_log() - local logfile = os.getenv('NVIM_LOG_FILE') or '.nvimlog' +function module.read_nvim_log(logfile) + logfile = logfile or os.getenv('NVIM_LOG_FILE') or '.nvimlog' local is_ci = module.isCI() local keep = is_ci and 999 or 10 local lines = module.read_file_list(logfile, -keep) or {} From a446fbc8fa278d9c1e4144dc9767c9dc0b184583 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 9 Feb 2020 00:05:48 -0800 Subject: [PATCH 6/7] lsp/rpc.lua: fix `env` application function Env vars must be merged with the current env. --- runtime/lua/vim/lsp/rpc.lua | 51 ++++++++------------ test/functional/fixtures/fake-lsp-server.lua | 4 +- 2 files changed, 22 insertions(+), 33 deletions(-) diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index e13b05610b..74d73da31f 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -33,38 +33,25 @@ local function convert_NIL(v) return v end --- If a dictionary is passed in, turn it into a list of string of "k=v" --- Accepts a table which can be composed of k=v strings or map-like --- specification, such as: --- --- ``` --- { --- "PRODUCTION=false"; --- "PATH=/usr/bin/"; --- PORT = 123; --- HOST = "0.0.0.0"; --- } --- ``` --- --- Non-string values will be cast with `tostring` -local function force_env_list(final_env) - if final_env then - local env = final_env - final_env = {} - for k,v in pairs(env) do - -- If it's passed in as a dict, then convert to list of "k=v" - if type(k) == "string" then - table.insert(final_env, k..'='..tostring(v)) - elseif type(v) == 'string' then - table.insert(final_env, v) - else - -- TODO is this right or should I exception here? - -- Try to coerce other values to string. - table.insert(final_env, tostring(v)) - end - end - return final_env +--- Merges current process env with the given env and returns the result as +--- a list of "k=v" strings. +--- +--- Example: +--- +--- { PRODUCTION="false", PATH="/usr/bin/", PORT=123, HOST="0.0.0.0", } +--- => { "PRODUCTION=false", "PATH=/usr/bin/", "PORT=123", "HOST=0.0.0.0", } +local function env_merge(env) + if env == nil then + return env end + -- Merge. + env = vim.tbl_extend('force', vim.fn.environ(), env) + local final_env = {} + for k,v in pairs(env) do + assert(type(k) == 'string', 'env must be a dict') + table.insert(final_env, k..'='..tostring(v)) + end + return final_env end local function format_message_with_content_length(encoded_message) @@ -262,7 +249,7 @@ local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_para if spawn_params.cwd then assert(is_dir(spawn_params.cwd), "cwd must be a directory") end - spawn_params.env = force_env_list(extra_spawn_params.env) + spawn_params.env = env_merge(extra_spawn_params.env) end handle, pid = uv.spawn(cmd, spawn_params, onexit) end diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua index 44117bea30..cac590fd14 100644 --- a/test/functional/fixtures/fake-lsp-server.lua +++ b/test/functional/fixtures/fake-lsp-server.lua @@ -369,7 +369,7 @@ function tests.basic_check_buffer_open_and_change_incremental() } end -function tests.basic_check_buffer_open_and_change_incremental_editting() +function tests.basic_check_buffer_open_and_change_incremental_editing() skeleton { on_init = function(params) local expected_capabilities = protocol.make_client_capabilities() @@ -422,6 +422,7 @@ local kill_timer = vim.loop.new_timer() kill_timer:start(_G.TIMEOUT or 1e3, 0, function() kill_timer:stop() kill_timer:close() + -- TODO: log('TIMEOUT') io.stderr:write("TIMEOUT") os.exit(100) end) @@ -432,6 +433,7 @@ local status, err = pcall(assert(tests[test_name], "Test not found")) kill_timer:stop() kill_timer:close() if not status then + -- TODO: log(err) io.stderr:write(err) os.exit(101) end From 6e13b9d26134210f0963341bd77c64a4437f37ec Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 16 Feb 2020 19:02:09 -0800 Subject: [PATCH 7/7] test/LSP: assert contents of log file --- src/nvim/msgpack_rpc/server.c | 2 +- test/busted/outputHandlers/TAP.lua | 2 +- test/busted/outputHandlers/nvim.lua | 2 +- test/functional/fixtures/fake-lsp-server.lua | 14 +++++++++++-- test/functional/plugin/lsp_spec.lua | 4 ++++ test/helpers.lua | 22 +++++++++++++++++--- 6 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c index 6168f097a7..062ea784ca 100644 --- a/src/nvim/msgpack_rpc/server.c +++ b/src/nvim/msgpack_rpc/server.c @@ -151,7 +151,7 @@ int server_start(const char *endpoint) result = socket_watcher_start(watcher, MAX_CONNECTIONS, connection_cb); if (result < 0) { - WLOG("Failed to start server: %s", uv_strerror(result)); + WLOG("Failed to start server: %s: %s", uv_strerror(result), watcher->addr); socket_watcher_close(watcher, free_server); return result; } diff --git a/test/busted/outputHandlers/TAP.lua b/test/busted/outputHandlers/TAP.lua index 8dc4ff55b6..5de48c0ad3 100644 --- a/test/busted/outputHandlers/TAP.lua +++ b/test/busted/outputHandlers/TAP.lua @@ -7,7 +7,7 @@ return function(options) local handler = require 'busted.outputHandlers.TAP'(options) local suiteEnd = function() - io.write(global_helpers.read_nvim_log()) + io.write(global_helpers.read_nvim_log(nil, true)) return nil, true end busted.subscribe({ 'suite', 'end' }, suiteEnd) diff --git a/test/busted/outputHandlers/nvim.lua b/test/busted/outputHandlers/nvim.lua index 8f3aad776e..5456e9ca98 100644 --- a/test/busted/outputHandlers/nvim.lua +++ b/test/busted/outputHandlers/nvim.lua @@ -196,7 +196,7 @@ return function(options) local tests = (testCount == 1 and 'test' or 'tests') local files = (fileCount == 1 and 'file' or 'files') io.write(globalTeardown) - io.write(global_helpers.read_nvim_log()) + io.write(global_helpers.read_nvim_log(nil, true)) io.write(suiteEndString:format(testCount, tests, fileCount, files, elapsedTime_ms)) io.write(getSummaryString()) io.flush() diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua index cac590fd14..dca7f35923 100644 --- a/test/functional/fixtures/fake-lsp-server.lua +++ b/test/functional/fixtures/fake-lsp-server.lua @@ -1,6 +1,16 @@ local protocol = require 'vim.lsp.protocol' +-- Logs to $NVIM_LOG_FILE. +-- +-- TODO(justinmk): remove after https://github.com/neovim/neovim/pull/7062 +local function log(loglevel, area, msg) + vim.fn.writefile( + {string.format('%s %s: %s', loglevel, area, msg)}, + vim.env.NVIM_LOG_FILE, + 'a') +end + local function message_parts(sep, ...) local parts = {} for i = 1, select("#", ...) do @@ -422,7 +432,7 @@ local kill_timer = vim.loop.new_timer() kill_timer:start(_G.TIMEOUT or 1e3, 0, function() kill_timer:stop() kill_timer:close() - -- TODO: log('TIMEOUT') + log('ERROR', 'LSP', 'TIMEOUT') io.stderr:write("TIMEOUT") os.exit(100) end) @@ -433,7 +443,7 @@ local status, err = pcall(assert(tests[test_name], "Test not found")) kill_timer:stop() kill_timer:close() if not status then - -- TODO: log(err) + log('ERROR', 'LSP', tostring(err)) io.stderr:write(err) os.exit(101) end diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index b2d00a400a..03e516d6f6 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -1,11 +1,13 @@ local helpers = require('test.functional.helpers')(after_each) +local assert_log = helpers.assert_log local clear = helpers.clear local buf_lines = helpers.buf_lines local dedent = helpers.dedent local exec_lua = helpers.exec_lua local eq = helpers.eq local eq_dumplog = helpers.eq_dumplog +local pesc = helpers.pesc local insert = helpers.insert local retry = helpers.retry local NIL = helpers.NIL @@ -229,6 +231,8 @@ describe('LSP', function() on_exit = function(code, signal) eq_dumplog(fake_lsp_logfile, 101, code, "exit code") -- See fake-lsp-server.lua eq_dumplog(fake_lsp_logfile, 0, signal, "exit signal") + assert_log(pesc([[assert_eq failed: left == "\"shutdown\"", right == "\"test\""]]), + fake_lsp_logfile) end; on_callback = function(...) eq(table.remove(expected_callbacks), {...}, "expected callback") diff --git a/test/helpers.lua b/test/helpers.lua index d9e1f4a963..a31a7733bf 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -82,6 +82,22 @@ function module.matches(pat, actual) error(string.format('Pattern does not match.\nPattern:\n%s\nActual:\n%s', pat, actual)) end +--- Asserts that `pat` matches one or more lines in the tail of $NVIM_LOG_FILE. +--- +--@param pat (string) Lua pattern to search for in the log file. +--@param logfile (string, default=$NVIM_LOG_FILE) full path to log file. +function module.assert_log(pat, logfile) + logfile = logfile or os.getenv('NVIM_LOG_FILE') or '.nvimlog' + local nrlines = 10 + local lines = module.read_file_list(logfile, -nrlines) or {} + for _,line in ipairs(lines) do + if line:match(pat) then return end + end + local logtail = module.read_nvim_log(logfile) + error(string.format('Pattern %q not found in log (last %d lines): %s:\n%s', + pat, nrlines, logfile, logtail)) +end + -- Invokes `fn` and returns the error string (may truncate full paths), or -- raises an error if `fn` succeeds. -- @@ -745,9 +761,9 @@ function module.isCI(name) end --- Gets the contents of `logfile` for printing to the build log. +-- Gets the (tail) contents of `logfile`. -- Also moves the file to "${NVIM_LOG_FILE}.displayed" on CI environments. -function module.read_nvim_log(logfile) +function module.read_nvim_log(logfile, ci_rename) logfile = logfile or os.getenv('NVIM_LOG_FILE') or '.nvimlog' local is_ci = module.isCI() local keep = is_ci and 999 or 10 @@ -759,7 +775,7 @@ function module.read_nvim_log(logfile) log = log..line..'\n' end log = log..('-'):rep(78)..'\n' - if is_ci then + if is_ci and ci_rename then os.rename(logfile, logfile .. '.displayed') end return log