diff --git a/lua/neotest-kotlin/command.lua b/lua/neotest-kotlin/command.lua index 16c5337..e05eaf5 100644 --- a/lua/neotest-kotlin/command.lua +++ b/lua/neotest-kotlin/command.lua @@ -1,11 +1,68 @@ local M = {} +---Finds the nearest parent directory containing a build.gradle or build.gradle.kts file +---@param filepath string the path to the file (e.g. test file) +---@return string|nil module_name the name of the gradle module, or nil if not found + +---Runs the Gradle printProjectPaths task and parses the output to map absolute paths to module names +---@param init_script_path string path to the init script +---@return table mapping from absolute dir to gradle module (e.g. /abs/path/app -> app) +local function get_gradle_project_paths(init_script_path) + local handle = io.popen( + string.format("./gradlew -I %s printProjectPaths", init_script_path) + ) + if not handle then + return {} + end + local output = handle:read("*a") + handle:close() + local map = {} + for line in output:gmatch("[^\n]+") do + local path, abs = line:match("NEOTEST_GRADLE_PROJECT%s+:(.-)%s+([^ ]+)$") + if not path then + -- Try with tab separator + local _, _, p, a = line:find("NEOTEST_GRADLE_PROJECT\t:?(.-)\t(.+)") + path, abs = p, a + end + if path and abs then + -- Remove leading : from path if present + path = path:gsub("^:", "") + map[abs] = path + end + end + return map +end + +---Finds the gradle module for a given file path using the mapping from get_gradle_project_paths +---@param filepath string +---@param project_map table +---@return string|nil module_name +local function find_gradle_module(filepath, project_map) + if project_map == nil or next(project_map) == nil then + return nil + end + + local sep = package.config:sub(1, 1) + local dir = filepath + while dir and dir ~= "." and dir ~= sep do + dir = dir:gsub(sep .. "[^" .. sep .. "]+$", "") + if project_map[dir] then + return project_map[dir] + end + if dir == "." or dir == sep or #dir < 2 then + break + end + end + return nil +end + ---Constructs the gradle command to execute ---@param tests string the name of the test block ---@param specs string the package name of the file you are interpreting ---@param outfile string where the test output will be written to. +---@param filepath string the path to the test file (to determine module) ---@return string command the gradle command to execute -function M.build(tests, specs, outfile) +function M.build(tests, specs, outfile, filepath) local INIT_SCRIPT_NAME = "test-logging.init.gradle.kts" local init_script_path = @@ -16,11 +73,27 @@ function M.build(tests, specs, outfile) ) end + local module = nil + if filepath then + local ok, project_map = pcall(get_gradle_project_paths, init_script_path) + if not ok then + error("Failed to get gradle project paths: " .. tostring(project_map)) + end + module = find_gradle_module(filepath, project_map) + end + + local gradle_cmd = "" + if module then + gradle_cmd = string.format(":%s:test", module) + else + gradle_cmd = "test" + end return string.format( - "kotest_filter_specs='%s' kotest_filter_tests='%s' ./gradlew -I %s test --console=plain | tee -a %s", + "kotest_filter_specs='%s' kotest_filter_tests='%s' ./gradlew -I %s %s --console=plain | tee -a %s", specs, tests, init_script_path, + gradle_cmd, outfile ) end diff --git a/lua/neotest-kotlin/init.lua b/lua/neotest-kotlin/init.lua index a21f47f..4ecd95b 100644 --- a/lua/neotest-kotlin/init.lua +++ b/lua/neotest-kotlin/init.lua @@ -102,7 +102,7 @@ function M.Adapter.build_spec(args) if pos.type == "dir" then local package = dir_determine_package(pos.path) .. ".*" - run_spec.command = command.build(tests, package, results_path) + run_spec.command = command.build(tests, package, results_path, pos.path) elseif pos.type == "file" or pos.type == "namespace" @@ -114,7 +114,7 @@ function M.Adapter.build_spec(args) treesitter.list_all_classes(pos.path)[1] ) - run_spec.command = command.build(tests, package, results_path) + run_spec.command = command.build(tests, package, results_path, pos.path) end print(run_spec.command) diff --git a/test-logging.init.gradle.kts b/test-logging.init.gradle.kts index aa2e842..e991f61 100644 --- a/test-logging.init.gradle.kts +++ b/test-logging.init.gradle.kts @@ -23,4 +23,14 @@ allprojects { } } } + + tasks.register("printProjectPaths") { + group = "help" + description = "Prints all project names and their absolute paths in a parseable format" + doLast { + allprojects.forEach { p -> + println("NEOTEST_GRADLE_PROJECT\t${p.path}\t${p.projectDir.absolutePath}") + } + } + } } diff --git a/tests/command_spec.lua b/tests/command_spec.lua index 8e6f0e9..2c1def5 100644 --- a/tests/command_spec.lua +++ b/tests/command_spec.lua @@ -1,11 +1,12 @@ local command = require("neotest-kotlin.command") describe("command", function() - it("valid", function() + it("valid without modules", function() local actual = command.build( "An example namespace", "com.codymikol.gummibear.pizza.FooClass", - "/tmp/results_example.txt" + "/tmp/results_example.txt", + "tests/com/codymikol/gummibear/pizza/FooClassTest.kt" ) local init_script_path =