From b4ccda54961fbf17bc49d536a4f58573d7c71321 Mon Sep 17 00:00:00 2001 From: Luca Negrini Date: Fri, 2 Jan 2026 10:57:53 +0100 Subject: [PATCH 01/15] fix: properly setting basedir --- .../command/junit_command_builder.lua | 4 ++++ lua/neotest-java/core/spec_builder/init.lua | 15 ++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lua/neotest-java/command/junit_command_builder.lua b/lua/neotest-java/command/junit_command_builder.lua index 1c07278..b950b78 100644 --- a/lua/neotest-java/command/junit_command_builder.lua +++ b/lua/neotest-java/command/junit_command_builder.lua @@ -117,6 +117,10 @@ CommandBuilder.build_junit = function(self, port) table.insert(junit_command.args, v) end + if self._basedir then + table.insert(junit_command.args, 1, "-Duser.dir=" .. self._basedir) + end + -- add debug arguments if debug port is specified if port then table.insert(junit_command.args, 1, "-Xdebug") diff --git a/lua/neotest-java/core/spec_builder/init.lua b/lua/neotest-java/core/spec_builder/init.lua index 98e5e38..48ea120 100644 --- a/lua/neotest-java/core/spec_builder/init.lua +++ b/lua/neotest-java/core/spec_builder/init.lua @@ -22,7 +22,7 @@ local client_provider = require("neotest-java.core.spec_builder.compiler.client_ --- @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 @@ -59,8 +59,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)) 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) @@ -104,10 +105,6 @@ function SpecBuilder.build_spec(args, config, deps) deps.mkdir(build_dir) deps.mkdir(build_dir:parent()) - -- JUNIT REPORT DIRECTORY - local reports_dir = deps.report_folder_name_gen(build_dir) - command:reports_dir(reports_dir) - local filepath = Path(position.path) local module = -- @@ -121,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 From 7f44be917e5946c92b7282cb354d099e4e89370f Mon Sep 17 00:00:00 2001 From: Luca Negrini Date: Fri, 2 Jan 2026 10:58:01 +0100 Subject: [PATCH 02/15] fix: improving selectors passing --- lua/neotest-java/command/junit_command_builder.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lua/neotest-java/command/junit_command_builder.lua b/lua/neotest-java/command/junit_command_builder.lua index b950b78..a7cb682 100644 --- a/lua/neotest-java/command/junit_command_builder.lua +++ b/lua/neotest-java/command/junit_command_builder.lua @@ -74,9 +74,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 .. "'") + table.insert(selectors, "--select-method=" .. v.method_name) + else + table.insert(selectors, "--select-class=" .. class_name) end end end From c66b80fa129110a4108fd6363b132e993bab3e2a Mon Sep 17 00:00:00 2001 From: Luca Negrini Date: Fri, 2 Jan 2026 11:08:07 +0100 Subject: [PATCH 03/15] fix: setting debug cwd and forward to repl --- lua/neotest-java/build_tool/init.lua | 18 +++++++++++++++++- lua/neotest-java/core/spec_builder/init.lua | 6 +++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/lua/neotest-java/build_tool/init.lua b/lua/neotest-java/build_tool/init.lua index e5bb455..e14d148 100644 --- a/lua/neotest-java/build_tool/init.lua +++ b/lua/neotest-java/build_tool/init.lua @@ -4,6 +4,7 @@ local log = require("neotest-java.logger") local nio = require("nio") local Job = require("plenary.job") local lib = require("neotest.lib") +local repl = require("dap.repl") ---@class neotest-java.BuildTool ---@field get_build_dirname fun(): neotest-java.Path @@ -25,8 +26,9 @@ end ---@param command string ---@param args string[] +---@param cwd string ---@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") @@ -36,14 +38,27 @@ build_tools.launch_debug_test = function(command, args) local stderr = {} local job = Job:new({ command = command, + cwd = cwd, args = args, on_stderr = function(_, data) + if data == nil then + return + end stderr[#stderr + 1] = data + vim.schedule(function() + repl.append(data) + end) end, on_stdout = function(_, data) + if data == nil then + return + end if string.find(data, "Listening") then test_command_started_listening.set() end + vim.schedule(function() + repl.append(data) + end) end, on_exit = function(_, code) terminated_command_event.set() @@ -56,6 +71,7 @@ build_tools.launch_debug_test = function(command, args) end, }) log.debug("starting job with command: ", command, " ", table.concat(args, " ")) + repl.clear() job:start() test_command_started_listening.wait() diff --git a/lua/neotest-java/core/spec_builder/init.lua b/lua/neotest-java/core/spec_builder/init.lua index 48ea120..e8a3fd9 100644 --- a/lua/neotest-java/core/spec_builder/init.lua +++ b/lua/neotest-java/core/spec_builder/init.lua @@ -152,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 { @@ -164,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, @@ -178,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 }, } From 1e738e847dd963c3bea28db18d321287eac8ab87 Mon Sep 17 00:00:00 2001 From: Luca Negrini Date: Fri, 2 Jan 2026 11:22:25 +0100 Subject: [PATCH 04/15] fix: handling multiple failures, even with no messages --- .../command/junit_command_builder.lua | 3 +- lua/neotest-java/core/spec_builder/init.lua | 2 +- lua/neotest-java/model/junit_result.lua | 84 ++++++++++++++----- 3 files changed, 66 insertions(+), 23 deletions(-) diff --git a/lua/neotest-java/command/junit_command_builder.lua b/lua/neotest-java/command/junit_command_builder.lua index a7cb682..bd96536 100644 --- a/lua/neotest-java/command/junit_command_builder.lua +++ b/lua/neotest-java/command/junit_command_builder.lua @@ -109,6 +109,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(), @@ -119,7 +120,7 @@ CommandBuilder.build_junit = function(self, port) end if self._basedir then - table.insert(junit_command.args, 1, "-Duser.dir=" .. self._basedir) + table.insert(junit_command.args, 1, "-Duser.dir=" .. self._basedir:to_string()) end -- add debug arguments if debug port is specified diff --git a/lua/neotest-java/core/spec_builder/init.lua b/lua/neotest-java/core/spec_builder/init.lua index e8a3fd9..7c4455f 100644 --- a/lua/neotest-java/core/spec_builder/init.lua +++ b/lua/neotest-java/core/spec_builder/init.lua @@ -60,7 +60,7 @@ local DEFAULT_DEPENDENCIES = { end, classpath_provider = ClasspathProvider({ client_provider = client_provider }), report_folder_name_gen = function(module_dir, build_dir) - local base = (module_dir and module_dir:append(build_dir)) or 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) diff --git a/lua/neotest-java/model/junit_result.lua b/lua/neotest-java/model/junit_result.lua index ef39264..8be9c66 100644 --- a/lua/neotest-java/model/junit_result.lua +++ b/lua/neotest-java/model/junit_result.lua @@ -79,8 +79,7 @@ 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 @@ -88,10 +87,21 @@ function JunitResult:status() -- 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 "", + 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 "", + failure_output = failed[1], + } + return FAILED, { fail } end return PASSED end @@ -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 + errors[i] = { message = failure_message, line = line } end - if with_name_prefix then - failure_message = self:name() .. " -> " .. failure_message - end - return { { message = failure_message, line = line } } + + return errors end ---@return string[] @@ -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 @@ -141,7 +162,17 @@ 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() + 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, @@ -187,7 +218,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 From ad7d611fc991ddd5eed1b0be123997df03b3d1d1 Mon Sep 17 00:00:00 2001 From: Luca Negrini Date: Fri, 2 Jan 2026 13:21:44 +0100 Subject: [PATCH 05/15] fix: refactored dir scanning for speedup --- lua/neotest-java/util/dir_scan.lua | 59 ++++++++---------------------- 1 file changed, 15 insertions(+), 44 deletions(-) diff --git a/lua/neotest-java/util/dir_scan.lua b/lua/neotest-java/util/dir_scan.lua index 702b0fd..88f7c45 100644 --- a/lua/neotest-java/util/dir_scan.lua +++ b/lua/neotest-java/util/dir_scan.lua @@ -39,52 +39,23 @@ local function scan(dir, opts, dependencies) dependencies = dependencies or {} iter_dir = dependencies.iter_dir or iter_dir - local seen = {} - --- @return {path: neotest-java.Path, typ: "directory" | "file"}[] - local find = function(_dir) - seen[_dir:to_string()] = true - return vim - -- - .iter(iter_dir(_dir)) - :totable() - end - - --- @type {path: neotest-java.Path, typ: "directory" | "file"}[] - local result = find(dir) - local all_seen = function() - return vim.iter(result) - :filter(function(result) - return result.typ == "directory" - end) - :all(function(obj) - local path_str = obj.path:to_string() - return seen[path_str] - end) + local stack = { dir } + local result = {} + local filter = contains(opts.search_patterns) + + while #stack > 0 do + local current_dir = table.remove(stack) + for entry in iter_dir(current_dir) do + if entry.typ == "directory" then + table.insert(stack, entry.path) + end + if filter(entry.path) then + table.insert(result, entry.path) + end + end end - local hard_limit = 10000 - while not all_seen() do - hard_limit = hard_limit - 1 - assert(hard_limit > 0, "Dir scan exceeded hard limit") - - local next_dir = vim.iter(result) - :filter(function(r) - return r.typ == "directory" - end) - :find(function(obj) - assert(obj.typ == "directory") - local path_str = obj.path:to_string() - return not seen[path_str] - end) - - result = vim.list_extend(result, find(next_dir.path)) - end - return vim.iter(result) - :map(function(obj) - return obj.path - end) - :filter(contains(opts.search_patterns)) - :totable() + return result end return scan From 316f641b3defa20223dc7c33fd9342749a30f653 Mon Sep 17 00:00:00 2001 From: Luca Negrini Date: Fri, 2 Jan 2026 16:10:54 +0100 Subject: [PATCH 06/15] test: support running from neotest-summary --- lua/neotest-java/build_tool/init.lua | 2 +- .../core/spec_builder/compiler/classpath_provider.lua | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/lua/neotest-java/build_tool/init.lua b/lua/neotest-java/build_tool/init.lua index e14d148..7a6a123 100644 --- a/lua/neotest-java/build_tool/init.lua +++ b/lua/neotest-java/build_tool/init.lua @@ -38,7 +38,7 @@ build_tools.launch_debug_test = function(command, args, cwd) local stderr = {} local job = Job:new({ command = command, - cwd = cwd, + cwd = cwd:to_string(), args = args, on_stderr = function(_, data) if data == nil then diff --git a/lua/neotest-java/core/spec_builder/compiler/classpath_provider.lua b/lua/neotest-java/core/spec_builder/compiler/classpath_provider.lua index 0ae8b91..c642bf4 100644 --- a/lua/neotest-java/core/spec_builder/compiler/classpath_provider.lua +++ b/lua/neotest-java/core/spec_builder/compiler/classpath_provider.lua @@ -13,20 +13,17 @@ local function ClasspathProvider(deps) get_classpath = function(base_dir, additional_classpath_entries) additional_classpath_entries = additional_classpath_entries or {} - local bufnr = vim.api.nvim_get_current_buf() - local bufname = vim.api.nvim_buf_get_name(bufnr) - local buffer_uri = vim.uri_from_fname(bufname) - + local base_dir_uri = vim.uri_from_fname(base_dir:to_string()) local client = deps.client_provider(base_dir) local response_for_runtime = client:request_sync("workspace/executeCommand", { command = "java.project.getClasspaths", - arguments = { buffer_uri, vim.json.encode({ scope = "runtime" }) }, + arguments = { base_dir_uri, vim.json.encode({ scope = "runtime" }) }, }) local response_for_test = client:request_sync("workspace/executeCommand", { command = "java.project.getClasspaths", - arguments = { buffer_uri, vim.json.encode({ scope = "test" }) }, + arguments = { base_dir_uri, vim.json.encode({ scope = "test" }) }, }) local additional_classpath_entries_strings = vim From e75a61e27782712582e30c112a3ecef0398445a6 Mon Sep 17 00:00:00 2001 From: Luca Negrini Date: Fri, 2 Jan 2026 17:14:53 +0100 Subject: [PATCH 07/15] test: updating tests --- lua/neotest-java/build_tool/init.lua | 19 ++++++++++++------- lua/neotest-java/model/junit_result.lua | 8 ++++++++ tests/unit/spec_builder_spec.lua | 19 +++++++++++-------- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/lua/neotest-java/build_tool/init.lua b/lua/neotest-java/build_tool/init.lua index 7a6a123..ac135e2 100644 --- a/lua/neotest-java/build_tool/init.lua +++ b/lua/neotest-java/build_tool/init.lua @@ -4,7 +4,8 @@ local log = require("neotest-java.logger") local nio = require("nio") local Job = require("plenary.job") local lib = require("neotest.lib") -local repl = require("dap.repl") + +local _, repl = pcall(require, "dap.repl") ---@class neotest-java.BuildTool ---@field get_build_dirname fun(): neotest-java.Path @@ -45,9 +46,11 @@ build_tools.launch_debug_test = function(command, args, cwd) return end stderr[#stderr + 1] = data - vim.schedule(function() - repl.append(data) - end) + if repl then + vim.schedule(function() + repl.append(data) + end) + end end, on_stdout = function(_, data) if data == nil then @@ -56,9 +59,11 @@ build_tools.launch_debug_test = function(command, args, cwd) if string.find(data, "Listening") then test_command_started_listening.set() end - vim.schedule(function() - repl.append(data) - end) + if repl then + vim.schedule(function() + repl.append(data) + end) + end end, on_exit = function(_, code) terminated_command_event.set() diff --git a/lua/neotest-java/model/junit_result.lua b/lua/neotest-java/model/junit_result.lua index 8be9c66..44a7eba 100644 --- a/lua/neotest-java/model/junit_result.lua +++ b/lua/neotest-java/model/junit_result.lua @@ -163,6 +163,14 @@ end ---@return neotest.Result function JunitResult:result() 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 diff --git a/tests/unit/spec_builder_spec.lua b/tests/unit/spec_builder_spec.lua index 4c9954f..cf35791 100644 --- a/tests/unit/spec_builder_spec.lua +++ b/tests/unit/spec_builder_spec.lua @@ -84,6 +84,7 @@ describe("SpecBuilder", function() eq({ command = vim.iter({ "java", + "-Duser.dir=.", "-Dspring.config.additional-location=" .. Path("src/main/resources/application.properties"):to_string(), "-jar", "my-junit-jar.jar", @@ -94,8 +95,8 @@ describe("SpecBuilder", function() "--disable-banner", "--details=testfeed", "--config=junit.platform.output.capture.stdout=true", - "--select-class='com.example.ExampleTest'", - "--select-method='com.example.ExampleTest#shouldNotFail()'", + "--config=junit.platform.output.capture.stderr=true", + "--select-method=com.example.ExampleTest#shouldNotFail()", }):join(" "), context = { reports_dir = Path("report_folder"), @@ -154,6 +155,7 @@ describe("SpecBuilder", function() eq({ command = vim.iter({ "java", + "-Duser.dir=/user/home/root", "-Dspring.config.additional-location=" .. Path("src/main/resources/application.properties"):to_string(), "-myExtraJvmArg", "-jar", @@ -165,13 +167,13 @@ describe("SpecBuilder", function() "--disable-banner", "--details=testfeed", "--config=junit.platform.output.capture.stdout=true", - "--select-class='com.example.ExampleTest'", - "--select-method='com.example.ExampleTest#shouldNotFail()'", + "--config=junit.platform.output.capture.stderr=true", + "--select-method=com.example.ExampleTest#shouldNotFail()", }):join(" "), context = { reports_dir = Path("report_folder"), }, - cwd = "root", + cwd = "/user/home/root", symbol = "shouldNotFail", }, actual) end) @@ -234,6 +236,7 @@ describe("SpecBuilder", function() eq({ command = vim.iter({ "java", + "-Duser.dir=/user/home/root/module-2", "-Dspring.config.additional-location=" .. Path("src/main/resources/application.properties"):to_string(), "-jar", "my-junit-jar.jar", @@ -244,13 +247,13 @@ describe("SpecBuilder", function() "--disable-banner", "--details=testfeed", "--config=junit.platform.output.capture.stdout=true", - "--select-class='com.example.ExampleInSecondModuleTest'", - "--select-method='com.example.ExampleInSecondModuleTest#shouldNotFail()'", + "--config=junit.platform.output.capture.stderr=true", + "--select-method=com.example.ExampleInSecondModuleTest#shouldNotFail()", }):join(" "), context = { reports_dir = Path("report_folder"), }, - cwd = "root", + cwd = "/user/home/root/module-2", symbol = "shouldNotFail", }, actual) end) From 4679bf9ea5aa6240fe1ae18a873c3e583b02ab36 Mon Sep 17 00:00:00 2001 From: Luca Negrini Date: Fri, 2 Jan 2026 17:24:08 +0100 Subject: [PATCH 08/15] chore: addressing copilot review --- lua/neotest-java/build_tool/init.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lua/neotest-java/build_tool/init.lua b/lua/neotest-java/build_tool/init.lua index ac135e2..4c7d0f9 100644 --- a/lua/neotest-java/build_tool/init.lua +++ b/lua/neotest-java/build_tool/init.lua @@ -27,7 +27,7 @@ end ---@param command string ---@param args string[] ----@param cwd string +---@param cwd neotest-java.Path ---@return nio.control.Event build_tools.launch_debug_test = function(command, args, cwd) lib.notify("Running debug test", vim.log.levels.INFO) @@ -76,7 +76,9 @@ build_tools.launch_debug_test = function(command, args, cwd) end, }) log.debug("starting job with command: ", command, " ", table.concat(args, " ")) - repl.clear() + if repl then + repl.clear() + end job:start() test_command_started_listening.wait() From 03be90857324d82971f57f3ad8d1590be9e8d789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Cas=C3=ADa?= <31012661+rcasia@users.noreply.github.com> Date: Fri, 2 Jan 2026 23:14:53 +0100 Subject: [PATCH 09/15] test: include uri verification for the classpath provider command --- tests/unit/classpath_provider_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/classpath_provider_spec.lua b/tests/unit/classpath_provider_spec.lua index 050d75d..6bd2aaf 100644 --- a/tests/unit/classpath_provider_spec.lua +++ b/tests/unit/classpath_provider_spec.lua @@ -16,7 +16,7 @@ describe("Classpath Provider", function() request_sync = function(_, method, params) eq(method, "workspace/executeCommand") eq(params.command, "java.project.getClasspaths") - -- eq(params.arguments[1], "test-uri") -- TODO: look at it later + eq(params.arguments[1], "file://some") assert(params.arguments[2], "Expected second argument to be present") local options = vim.json.decode(params.arguments[2]) From 04fd48c259168a1c008b1a4ef0c61da87269d3e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Cas=C3=ADa?= <31012661+rcasia@users.noreply.github.com> Date: Fri, 2 Jan 2026 23:29:17 +0100 Subject: [PATCH 10/15] test: wrap expected user.dir into a Path struct --- tests/unit/spec_builder_spec.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/spec_builder_spec.lua b/tests/unit/spec_builder_spec.lua index cf35791..6b52ec4 100644 --- a/tests/unit/spec_builder_spec.lua +++ b/tests/unit/spec_builder_spec.lua @@ -84,7 +84,7 @@ describe("SpecBuilder", function() eq({ command = vim.iter({ "java", - "-Duser.dir=.", + "-Duser.dir=" .. Path("."):to_string(), "-Dspring.config.additional-location=" .. Path("src/main/resources/application.properties"):to_string(), "-jar", "my-junit-jar.jar", @@ -155,7 +155,7 @@ describe("SpecBuilder", function() eq({ command = vim.iter({ "java", - "-Duser.dir=/user/home/root", + "-Duser.dir=" .. Path("/user/home/root"):to_string(), "-Dspring.config.additional-location=" .. Path("src/main/resources/application.properties"):to_string(), "-myExtraJvmArg", "-jar", @@ -236,7 +236,7 @@ describe("SpecBuilder", function() eq({ command = vim.iter({ "java", - "-Duser.dir=/user/home/root/module-2", + "-Duser.dir=" .. Path("/user/home/root/module-2"):to_string(), "-Dspring.config.additional-location=" .. Path("src/main/resources/application.properties"):to_string(), "-jar", "my-junit-jar.jar", From 5fb1ebc00c1d549fbe7125dfe3f53869f7d120c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Cas=C3=ADa?= <31012661+rcasia@users.noreply.github.com> Date: Fri, 2 Jan 2026 23:33:53 +0100 Subject: [PATCH 11/15] test: wrap expected cwd into a Path struct --- tests/unit/spec_builder_spec.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/spec_builder_spec.lua b/tests/unit/spec_builder_spec.lua index 6b52ec4..26ed65f 100644 --- a/tests/unit/spec_builder_spec.lua +++ b/tests/unit/spec_builder_spec.lua @@ -101,7 +101,7 @@ describe("SpecBuilder", function() context = { reports_dir = Path("report_folder"), }, - cwd = ".", + cwd = Path("."):to_string(), symbol = "shouldNotFail", }, actual) end) @@ -173,7 +173,7 @@ describe("SpecBuilder", function() context = { reports_dir = Path("report_folder"), }, - cwd = "/user/home/root", + cwd = Path("/user/home/root"):to_string(), symbol = "shouldNotFail", }, actual) end) @@ -253,7 +253,7 @@ describe("SpecBuilder", function() context = { reports_dir = Path("report_folder"), }, - cwd = "/user/home/root/module-2", + cwd = Path("/user/home/root/module-2"):to_string(), symbol = "shouldNotFail", }, actual) end) From 1e4424de5cb446aa1457ae73607fe188a011fe06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Cas=C3=ADa?= <31012661+rcasia@users.noreply.github.com> Date: Fri, 2 Jan 2026 23:41:07 +0100 Subject: [PATCH 12/15] refactor: bring Duser.dir arg closer to jvm_args --- lua/neotest-java/command/junit_command_builder.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lua/neotest-java/command/junit_command_builder.lua b/lua/neotest-java/command/junit_command_builder.lua index bd96536..b06e526 100644 --- a/lua/neotest-java/command/junit_command_builder.lua +++ b/lua/neotest-java/command/junit_command_builder.lua @@ -96,6 +96,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 = java(), args = vim.iter({ @@ -119,10 +123,6 @@ CommandBuilder.build_junit = function(self, port) table.insert(junit_command.args, v) end - if self._basedir then - table.insert(junit_command.args, 1, "-Duser.dir=" .. self._basedir:to_string()) - end - -- add debug arguments if debug port is specified if port then table.insert(junit_command.args, 1, "-Xdebug") From 55908a9be2dd8741fc035c59cdaafd6314bb6803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Cas=C3=ADa?= <31012661+rcasia@users.noreply.github.com> Date: Sat, 3 Jan 2026 00:12:20 +0100 Subject: [PATCH 13/15] docs: correct docstring hints in dir_scan --- lua/neotest-java/util/dir_scan.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lua/neotest-java/util/dir_scan.lua b/lua/neotest-java/util/dir_scan.lua index 88f7c45..ffa3776 100644 --- a/lua/neotest-java/util/dir_scan.lua +++ b/lua/neotest-java/util/dir_scan.lua @@ -1,5 +1,9 @@ +--- @class neotest-java.DirScanResultItem +--- @field path neotest-java.Path +--- @field typ "directory" | "file" + --- @param dir neotest-java.Path ---- @return fun(): { path: neotest-java.Path, typ: "directory" | "file" } | nil +--- @return fun(): neotest-java.DirScanResultItem | nil local iter_dir = function(dir) local handle = assert(vim.uv.fs_scandir(dir:to_string())) @@ -29,7 +33,7 @@ local contains = function(patterns) end ---@class neotest-java.DirScanDependencies ----@field iter_dir fun(dir: neotest-java.Path): fun(): string | nil +---@field iter_dir fun(dir: neotest-java.Path): fun(): neotest-java.DirScanResultItem | nil ---@param dir neotest-java.Path ---@param opts? { search_patterns: string[] } From 98e745295475f48de6923de3aa88084cb24ae35a Mon Sep 17 00:00:00 2001 From: Luca Negrini Date: Mon, 5 Jan 2026 13:04:39 +0100 Subject: [PATCH 14/15] fix: restoring quotes on test selectors --- lua/neotest-java/command/junit_command_builder.lua | 4 ++-- tests/unit/spec_builder_spec.lua | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lua/neotest-java/command/junit_command_builder.lua b/lua/neotest-java/command/junit_command_builder.lua index 1ef3bb4..97889c0 100644 --- a/lua/neotest-java/command/junit_command_builder.lua +++ b/lua/neotest-java/command/junit_command_builder.lua @@ -81,9 +81,9 @@ CommandBuilder.build_junit = function(self, port) if v.type == "test" then local class_name = v.qualified_name:match("^(.-)#") or v.qualified_name if v.method_name then - table.insert(selectors, "--select-method=" .. v.method_name) + table.insert(selectors, "--select-method='" .. v.method_name .. "'") else - table.insert(selectors, "--select-class=" .. class_name) + table.insert(selectors, "--select-class='" .. class_name .. "'") end end end diff --git a/tests/unit/spec_builder_spec.lua b/tests/unit/spec_builder_spec.lua index 8002a9d..f22ad5a 100644 --- a/tests/unit/spec_builder_spec.lua +++ b/tests/unit/spec_builder_spec.lua @@ -101,7 +101,7 @@ describe("SpecBuilder", function() "--details=testfeed", "--config=junit.platform.output.capture.stdout=true", "--config=junit.platform.output.capture.stderr=true", - "--select-method=com.example.ExampleTest#shouldNotFail()", + "--select-method='com.example.ExampleTest#shouldNotFail()'", }):join(" "), context = { reports_dir = Path("report_folder"), @@ -178,7 +178,7 @@ describe("SpecBuilder", function() "--details=testfeed", "--config=junit.platform.output.capture.stdout=true", "--config=junit.platform.output.capture.stderr=true", - "--select-method=com.example.ExampleTest#shouldNotFail()", + "--select-method='com.example.ExampleTest#shouldNotFail()'", }):join(" "), context = { reports_dir = Path("report_folder"), @@ -263,7 +263,7 @@ describe("SpecBuilder", function() "--details=testfeed", "--config=junit.platform.output.capture.stdout=true", "--config=junit.platform.output.capture.stderr=true", - "--select-method=com.example.ExampleInSecondModuleTest#shouldNotFail()", + "--select-method='com.example.ExampleInSecondModuleTest#shouldNotFail()'", }):join(" "), context = { reports_dir = Path("report_folder"), From 4c45ff183d26a08a4d906d406d1478241019dea4 Mon Sep 17 00:00:00 2001 From: Luca Negrini Date: Mon, 5 Jan 2026 15:17:25 +0100 Subject: [PATCH 15/15] test: added multiple failures test --- tests/unit/result_builder_spec.lua | 77 +++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/tests/unit/result_builder_spec.lua b/tests/unit/result_builder_spec.lua index 00cbfc1..a5bf69a 100644 --- a/tests/unit/result_builder_spec.lua +++ b/tests/unit/result_builder_spec.lua @@ -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) @@ -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 = [[ + + + + FAILURE OUTPUT + + + + + FAILURE OUTPUT + + + FAILURE OUTPUT + + + + ]] + + 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: but was: " } }, + short = "test1() -> expected: but was: ", + status = "failed", + output = TEMPNAME, + }, + ["com.example.TwoTests#test2"] = { + errors = { + { message = "test2() -> expected: but was: " }, + { message = "test2() -> java.lang.StackOverflowError" }, + }, + short = "test2() -> expected: but was: \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)