Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion lua/neotest-java/build_tool/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ local nio = require("nio")
local Job = require("plenary.job")
local lib = require("neotest.lib")

local _, repl = pcall(require, "dap.repl")

---@class neotest-java.BuildTool
---@field get_build_dirname fun(): neotest-java.Path
---@field get_project_filename fun(): string
Expand All @@ -25,8 +27,9 @@ end

---@param command string
---@param args string[]
---@param cwd neotest-java.Path
---@return nio.control.Event
build_tools.launch_debug_test = function(command, args)
build_tools.launch_debug_test = function(command, args, cwd)
lib.notify("Running debug test", vim.log.levels.INFO)
log.trace("run_debug_test function")

Expand All @@ -36,14 +39,31 @@ build_tools.launch_debug_test = function(command, args)
local stderr = {}
local job = Job:new({
command = command,
cwd = cwd:to_string(),
args = args,
on_stderr = function(_, data)
if data == nil then
return
end
stderr[#stderr + 1] = data
if repl then
vim.schedule(function()
repl.append(data)
end)
end
end,
on_stdout = function(_, data)
if data == nil then
return
end
if string.find(data, "Listening") then
test_command_started_listening.set()
end
if repl then
vim.schedule(function()
repl.append(data)
end)
end
end,
on_exit = function(_, code)
terminated_command_event.set()
Expand All @@ -56,6 +76,9 @@ build_tools.launch_debug_test = function(command, args)
end,
})
log.debug("starting job with command: ", command, " ", table.concat(args, " "))
if repl then
repl.clear()
end
job:start()
test_command_started_listening.wait()

Expand Down
8 changes: 7 additions & 1 deletion lua/neotest-java/command/junit_command_builder.lua
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,10 @@ CommandBuilder.build_junit = function(self, port)
for _, v in ipairs(self._test_references) do
if v.type == "test" then
local class_name = v.qualified_name:match("^(.-)#") or v.qualified_name
table.insert(selectors, "--select-class='" .. class_name .. "'")
if v.method_name then
table.insert(selectors, "--select-method='" .. v.method_name .. "'")
else
table.insert(selectors, "--select-class='" .. class_name .. "'")
end
end
end
Expand All @@ -101,6 +102,10 @@ CommandBuilder.build_junit = function(self, port)
unpack(self._jvm_args),
}

if self._basedir then
table.insert(jvm_args, 1, "-Duser.dir=" .. self._basedir:to_string())
end

local junit_command = {
command = self._java_bin:to_string(),
args = vim.iter({
Expand All @@ -114,6 +119,7 @@ CommandBuilder.build_junit = function(self, port)
"--disable-banner",
"--details=testfeed",
"--config=junit.platform.output.capture.stdout=true",
"--config=junit.platform.output.capture.stderr=true",
})
:flatten()
:totable(),
Expand Down
17 changes: 11 additions & 6 deletions lua/neotest-java/core/spec_builder/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ local Binaries = require("neotest-java.command.binaries")
--- @field scan fun(base_dir: neotest-java.Path, opts: { search_patterns: string[] }): neotest-java.Path[]
--- @field compile fun(cwd: neotest-java.Path, compile_mode: string)
--- @field classpath_provider neotest-java.ClasspathProvider
--- @field report_folder_name_gen fun(build_dir: neotest-java.Path): neotest-java.Path
--- @field report_folder_name_gen fun(module_dir: neotest-java.Path, build_dir: neotest-java.Path): neotest-java.Path
--- @field build_tool_getter fun(project_type: string): neotest-java.BuildTool
--- @field detect_project_type fun(base_dir: neotest-java.Path): string
--- @field binaries neotest-java.LspBinaries
Expand Down Expand Up @@ -51,8 +51,9 @@ local DEFAULT_DEPENDENCIES = {
})
end,
classpath_provider = ClasspathProvider({ client_provider = client_provider }),
report_folder_name_gen = function(build_dir)
return build_dir:append("junit-reports"):append(nio.fn.strftime("%d%m%y%H%M%S"))
report_folder_name_gen = function(module_dir, build_dir)
local base = (module_dir and module_dir:append(build_dir:to_string())) or build_dir
return base:append("junit-reports"):append(nio.fn.strftime("%d%m%y%H%M%S"))
end,
build_tool_getter = function(project_type)
return build_tools.get(project_type)
Expand Down Expand Up @@ -117,6 +118,10 @@ function SpecBuilder.build_spec(args, config, deps)

command:basedir(module.base_dir)

-- JUNIT REPORT DIRECTORY
local reports_dir = deps.report_folder_name_gen(module.base_dir, build_dir)
command:reports_dir(reports_dir)

command:spring_property_filepaths(build_tool.get_spring_property_filepaths(project:get_module_dirs()))

-- TEST SELECTORS
Expand Down Expand Up @@ -147,7 +152,7 @@ function SpecBuilder.build_spec(args, config, deps)
-- PREPARE DEBUG TEST COMMAND
local junit = command:build_junit(port)
logger.debug("junit debug command: ", junit.command, " ", table.concat(junit.args, " "))
local terminated_command_event = build_tools.launch_debug_test(junit.command, junit.args)
local terminated_command_event = build_tools.launch_debug_test(junit.command, junit.args, module.base_dir)

local project_name = vim.fn.fnamemodify(root:to_string(), ":t")
return {
Expand All @@ -159,7 +164,7 @@ function SpecBuilder.build_spec(args, config, deps)
port = port,
projectName = project_name,
},
cwd = root:to_string(),
cwd = module.base_dir:to_string(),
symbol = position.type == "test" and position.name or nil,
context = {
strategy = args.strategy,
Expand All @@ -173,7 +178,7 @@ function SpecBuilder.build_spec(args, config, deps)
logger.info("junit command: ", command:build_to_string())
return {
command = command:build_to_string(),
cwd = root:to_string(),
cwd = module.base_dir:to_string(),
symbol = position.name,
context = { reports_dir = reports_dir },
}
Expand Down
92 changes: 71 additions & 21 deletions lua/neotest-java/model/junit_result.lua
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,29 @@ function JunitResult:classname()
end

---@return neotest.ResultStatus
---@return string | nil failure_message a short output
---@return string | nil failure_output a more detailed output
---@return table an array-like table containing tables of the form {failure_message: string, failure_output: string}
function JunitResult:status()
local failed = self.testcase.failure or self.testcase.error
-- This is not parsed correctly by the library
-- <failure message="expected: &lt;1> but was: &lt;2>" type="org.opentest4j.AssertionFailedError">
-- it breaks in the first '>'
-- so it does not detect message attribute sometimes
if failed and not failed._attr then
return FAILED, failed[1], failed[2]
local failures = {}
for i, fail in ipairs(failed) do
failures[i] = {
failure_message = fail._attr.message or fail._attr.type or "<unknown failure>",
failure_output = fail[1],
}
end
return FAILED, failures
end
if failed and failed._attr then
return FAILED, failed._attr.message, failed[1]
local fail = {
failure_message = failed._attr.message or failed._attr.type or "<unknown failure>",
failure_output = failed[1],
}
return FAILED, { fail }
end
return PASSED
end
Expand All @@ -100,24 +110,32 @@ end
---@return neotest.Error[] | nil
function JunitResult:errors(with_name_prefix)
with_name_prefix = with_name_prefix or false
local status, failure_message, failure_output = self:status()
local status, failures = self:status()
if status == PASSED then
return nil
end

local filename = string.match(self:classname(), "[%.]?([%a%$_][%a%d%$_]+)$") .. ".java"
local line_searchpattern = string.gsub(filename, "%.", "%%.") .. ":(%d+)%)"
local errors = {}

for i, failure in ipairs(failures) do
local line
if failure.failure_output then
line = string.match(failure.failure_output, line_searchpattern)
-- NOTE: errors array is expecting lines properties to be 0 index based
line = line and line - 1 or nil
end

local line
if failure_output then
line = string.match(failure_output, line_searchpattern)
-- NOTE: errors array is expecting lines properties to be 0 index based
line = line and line - 1 or nil
end
local failure_message = failure.failure_message
if with_name_prefix then
failure_message = self:name() .. " -> " .. failure_message
end

if status == PASSED then
return nil
end
if with_name_prefix then
failure_message = self:name() .. " -> " .. failure_message
errors[i] = { message = failure_message, line = line }
end
return { { message = failure_message, line = line } }

return errors
end

---@return string[]
Expand All @@ -127,9 +145,12 @@ function JunitResult:output()
system_out = { system_out }
end

local status, _, failure_output = self:status()
local status, failures = self:status()
if status == FAILED then
system_out[#system_out + 1] = failure_output
for _, failure in ipairs(failures) do
system_out[#system_out + 1] = failure.failure_output
system_out[#system_out + 1] = NEW_LINE
end
else -- PASSED
system_out[#system_out + 1] = "Test passed" .. NEW_LINE
end
Expand All @@ -141,7 +162,25 @@ end
--- Each time this function is called, it will create a temporary file with the output content
---@return neotest.Result
function JunitResult:result()
local status, failure_message = self:status()
local status, failures = self:status()

if status == PASSED then
return {
status = status,
output = create_file_with_content(self:output()),
}
end

local failure_message = ""
if failures then
for i, failure in ipairs(failures) do
failure_message = failure_message .. failure.failure_message
if i < #failures then
failure_message = failure_message .. NEW_LINE
end
end
end

return {
status = status,
short = failure_message,
Expand Down Expand Up @@ -187,7 +226,18 @@ function JunitResult.merge_results(results)
return result:errors(), result:name()
end)
:map(function(error, name)
return name .. " -> " .. error[1].message
if #error == 1 then
return name .. " -> " .. error[1].message
end

local errs = name .. " -> {" .. NEW_LINE
for i, err in ipairs(error) do
errs = errs .. err.message
if i < #error then
errs = errs .. NEW_LINE
end
end
return errs .. NEW_LINE .. "}"
end)
:fold(nil, function(a, b)
if not a then
Expand Down
77 changes: 76 additions & 1 deletion tests/unit/result_builder_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,6 @@ describe("ResultBuilder", function()
local results = result_builder.build_results(DEFAULT_SPEC, SUCCESSFUL_RESULT, tree, scan_dir, read_file)

--then
print(vim.inspect({ results = results, expected = expected }))
assert.are.same(expected, results)
end)

Expand Down Expand Up @@ -520,4 +519,80 @@ describe("ResultBuilder", function()
--then
assert.are.same(expected, results)
end)

async.it("should build results with multiple failures", function()
local file_content = [[
package com.example;

import org.junit.jupiter.Test;

import static org.junit.jupiter.api.Assertions.assertFalse;

public class TwoTests {

@Test
void test1() {
assertFalse(true);
}

@Test
void test2() {
assertFalse(true);
}
}
]]

local report_file = [[
<testsuite>
<testcase name="test1()" classname="com.example.TwoTests" time="0.003">
<failure message="expected: &lt;false&gt; but was: &lt;true&gt;" type="org.opentest4j.AssertionFailedError">
FAILURE OUTPUT
</failure>
</testcase>
<testcase name="test2()" classname="com.example.TwoTests" time="0.003">
<failure message="expected: &lt;false&gt; but was: &lt;true&gt;" type="org.opentest4j.AssertionFailedError">
FAILURE OUTPUT
</failure>
<failure type="java.lang.StackOverflowError">
FAILURE OUTPUT
</failure>
</testcase>
</testsuite>
]]

local file_path = create_tempfile_with_test(file_content)

local tree = plugin.discover_positions(file_path:to_string())
local scan_dir = function(dir)
assert(dir == DEFAULT_SPEC.context.reports_dir, "should scan in spec.context.reports_dir")
return { file_path }
end
local read_file = function()
return report_file
end

local expected = {
["com.example.TwoTests#test1"] = {
errors = { { message = "test1() -> expected: <false> but was: <true>" } },
short = "test1() -> expected: <false> but was: <true>",
status = "failed",
output = TEMPNAME,
},
["com.example.TwoTests#test2"] = {
errors = {
{ message = "test2() -> expected: <false> but was: <true>" },
{ message = "test2() -> java.lang.StackOverflowError" },
},
short = "test2() -> expected: <false> but was: <true>\njava.lang.StackOverflowError",
status = "failed",
output = TEMPNAME,
},
}

--when
local results = result_builder.build_results(DEFAULT_SPEC, SUCCESSFUL_RESULT, tree, scan_dir, read_file)

--then
assert.are.same(expected, results)
end)
end)
Loading