Add test result parsing and display via diagnostic for testng

This commit is contained in:
roland.hilliges 2023-09-04 14:41:19 -06:00 committed by Mathias Fußenegger
parent 697b39e3db
commit 3ca419c52a
2 changed files with 178 additions and 16 deletions

View file

@ -123,7 +123,7 @@ local TestLevel = {
}
local function make_junit_request_args(lens, uri)
local function make_request_args(lens, uri)
local methodname = ''
local name_parts = vim.split(lens.fullName, '#')
local classname = name_parts[1]
@ -227,7 +227,7 @@ end
local function fetch_launch_args(lens, context, on_launch_args)
local req_arguments = make_junit_request_args(lens, context.uri)
local req_arguments = make_request_args(lens, context.uri)
local cmd_junit_args = {
command = 'vscode.java.test.junit.argument';
arguments = { vim.fn.json_encode(req_arguments) };
@ -308,6 +308,29 @@ local function get_first_class_lens(lenses)
end
end
--- Return path to com.microsoft.java.test.runner-jar-with-dependencies.jar if found in bundles
---
---@return string? path
local function testng_runner()
local vscode_runner = 'com.microsoft.java.test.runner-jar-with-dependencies.jar'
local client = get_clients({name='jdtls'})[1]
local bundles = client and client.config.init_options.bundles or {}
for _, jar_path in pairs(bundles) do
local parts = vim.split(jar_path, '/')
if parts[#parts] == vscode_runner then
return jar_path
end
local basepath = vim.fs.dirname(jar_path)
if basepath then
for name, _ in vim.fs.dir(basepath) do
if name == vscode_runner then
return vim.fs.joinpath(basepath, name)
end
end
end
end
return nil
end
local function make_config(lens, launch_args, config_overrides)
local config = {
@ -324,14 +347,25 @@ local function make_config(lens, launch_args, config_overrides)
}
config = vim.tbl_extend('force', config, config_overrides or default_config_overrides)
if lens.testKind == TestKind.TestNG or lens.kind == TestKind.TestNG then
config.mainClass = 'org.testng.TestNG'
-- id is in the format <project>@<class>#<method>
local parts = vim.split(lens.id, '@')
parts = vim.split(parts[2], '#')
if #parts > 1 then
config.args = string.format('-testclass %s -methods %s.%s', parts[1], parts[1], parts[2])
local jar = testng_runner()
if jar then
config.mainClass = 'com.microsoft.java.test.runner.Launcher'
config.args = string.format('testng %s', lens.fullName)
table.insert(config.classPaths, jar);
else
config.args = string.format('-testclass %s', parts[1])
local msg = (
"Using basic TestNG integration. "
.. "For better test results add com.microsoft.java.test.runner-jar-with-dependencies.jar to one of the `bundles` folders")
vim.notify(msg)
config.mainClass = 'org.testng.TestNG'
-- id is in the format <project>@<class>#<method>
local parts = vim.split(lens.id, '@')
parts = vim.split(parts[2], '#')
if #parts > 1 then
config.args = string.format('-testclass %s -methods %s.%s', parts[1], parts[1], parts[2])
else
config.args = string.format('-testclass %s', parts[1])
end
end
else
config.args = table.concat(launch_args.programArguments, ' ');
@ -340,6 +374,7 @@ local function make_config(lens, launch_args, config_overrides)
end
---@param bufnr? integer
---@return JdtDapContext
local function make_context(bufnr)
@ -378,19 +413,47 @@ local function run(lens, config, context, opts)
config = vim.tbl_extend('force', config, opts.config or {})
local test_results
local server = nil
local junit = require('jdtls.junit')
if lens.kind == TestKind.TestNG then
dap.run(config, {
after = function()
if lens.kind == TestKind.TestNG or lens.testKind == TestKind.TestNG then
local testng = require('jdtls.testng')
local run_opts = {}
if config.mainClass == "com.microsoft.java.test.runner.Launcher" then
function run_opts.before(conf)
server = assert(uv.new_tcp(), "uv.new_tcp() must return handle")
test_results = testng.mk_test_results(context.bufnr)
server:bind('127.0.0.1', 0)
server:listen(128, function(err2)
assert(not err2, err2)
local sock = assert(vim.loop.new_tcp(), "uv.new_tcp must return handle")
server:accept(sock)
sock:read_start(test_results.mk_reader(sock))
end)
conf.args = string.format('%s %s', server:getsockname().port, conf.args)
return conf
end
function run_opts.after()
if server then
server:shutdown()
server:close()
end
test_results.show(lens, context)
if opts.after_test then
opts.after_test()
end
end
})
else
function run_opts.after()
if opts.after_test then
opts.after_test()
end
end
end
dap.run(config, run_opts)
return
end
local junit = require('jdtls.junit')
dap.run(config, {
before = function(conf)
server = assert(uv.new_tcp(), "uv.new_tcp() must return handle")
@ -406,8 +469,10 @@ local function run(lens, config, context, opts)
return conf
end;
after = function()
server:shutdown()
server:close()
if server then
server:shutdown()
server:close()
end
local items = test_results.show()
maybe_repeat(lens, config, context, opts, items)
if opts.after_test then

97
lua/jdtls/testng.lua Normal file
View file

@ -0,0 +1,97 @@
local ns = vim.api.nvim_create_namespace('testng')
local M = {}
local function parse(content, tests)
local lines = vim.split(content, '\n')
for _, line in ipairs(lines) do
if vim.startswith(line, '@@<TestRunner-') then
line = line.sub(line, 15)
line = line:sub(1, -13)
local test = vim.json.decode(line)
if test.name ~= 'testStarted' then
table.insert(tests, test)
end
end
end
end
M.__parse = parse
local function mk_buf_loop(sock, handle_buffer)
local buffer = ''
return function(err, chunk)
assert(not err, err)
if chunk then
buffer = buffer .. chunk
else
sock:close()
handle_buffer(buffer)
end
end
end
function M.mk_test_results(bufnr)
vim.api.nvim_buf_clear_namespace(bufnr, ns, 0, -1)
local tests = {}
local handle_buffer = function(buf)
parse(buf, tests)
end
local function get_test_line_nr(lenses, name)
if lenses.fullName == name then
return lenses.range.start.line
end
for _, v in ipairs(lenses) do
if v.fullName == name then
return v.range.start.line
end
end
return nil
end
return {
show = function(lens)
local repl = require('dap.repl')
-- error = '✘',
-- warn = '▲',
-- hint = '⚑',
-- info = '»'
local lenses = lens.children or lens
local failed = {}
for _, test in ipairs(tests) do
local lnum = get_test_line_nr(lenses, test.attributes.name)
if lnum ~= nil then
local testName = vim.split(test.attributes.name, '#')[2]
local message = test.attributes.message or 'test failed'
if test.name == 'testFailed' then
table.insert(failed, {
bufnr = bufnr,
lnum = lnum,
col = 0,
severity = vim.diagnostic.severity.ERROR,
source = 'testng',
message = message,
user_data = {}
})
repl.append('' .. testName .. ' failed')
repl.append(message)
repl.append(test.attributes.trace)
elseif test.name == 'testFinished' then
local text = { '✔️ ' }
vim.api.nvim_buf_set_extmark(bufnr, ns, lnum, 0, {
virt_text = { text },
})
repl.append('✔️ ' .. testName .. ' passed')
end
end
end
vim.diagnostic.set(ns, bufnr, failed, {})
end,
mk_reader = function(sock)
return vim.schedule_wrap(mk_buf_loop(sock, handle_buffer))
end,
}
end
return M