diff --git a/README.md b/README.md index c2eb206..05ce731 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ src="https://github.com/user-attachments/assets/7e297d7a-f06d-44a9-adef-92131185e8ca" />

- # Neotest VSTest Neotest adapter for dotnet @@ -64,34 +63,37 @@ require("neotest").setup({ The adapter optionally supports extra settings: ```lua +-- NOTE: This should be set before calling require("neotest-vstest") +vim.g.neotest_vstest = { + -- Path to dotnet sdk path. + -- Used in cases where the sdk path cannot be auto discovered. + sdk_path = "/usr/local/dotnet/sdk/9.0.101/", + -- table is passed directly to DAP when debugging tests. + dap_settings = { + type = "netcoredbg", + }, + -- If multiple solutions exists the adapter will ask you to choose one. + -- If you have a different heuristic for choosing a solution you can provide a function here. + solution_selector = function(solutions) + return nil -- return the solution you want to use or nil to let the adapter choose. + end, + -- If multiple .runsettings/testconfig.json files are present in the test project directory + -- you will be given the choice of file to use when setting up the adapter. + -- Or you can provide a function here + -- default nil to select from all files in project directory + settings_selector = function(project_dir) + return nil -- return the .runsettings/testconfig.json file you want to use or let the adapter choose + end, + build_opts = { + -- Arguments that will be added to all `dotnet build` and `dotnet msbuild` commands + additional_args = {} + }, + timeout_ms = 30 * 5 * 1000 -- number of milliseconds to wait before timeout while communicating with adapter client +} + require("neotest").setup({ adapters = { - require("neotest-vstest")({ - -- Path to dotnet sdk path. - -- Used in cases where the sdk path cannot be auto discovered. - sdk_path = "/usr/local/dotnet/sdk/9.0.101/", - -- table is passed directly to DAP when debugging tests. - dap_settings = { - type = "netcoredbg", - }, - -- If multiple solutions exists the adapter will ask you to choose one. - -- If you have a different heuristic for choosing a solution you can provide a function here. - solution_selector = function(solutions) - return nil -- return the solution you want to use or nil to let the adapter choose. - end, - -- If multiple .runsettings/testconfig.json files are present in the test project directory - -- you will be given the choice of file to use when setting up the adapter. - -- Or you can provide a function here - -- default nil to select from all files in project directory - settings_selector = function(project_dir) - return nil -- return the .runsettings/testconfig.json file you want to use or let the adapter choose - end, - build_opts = { - -- Arguments that will be added to all `dotnet build` and `dotnet msbuild` commands - additional_args = {} - }, - timeout_ms = 30 * 5 * 1000 -- number of milliseconds to wait before timeout while communicating with adapter client - }) + require("neotest-vstest") } }) ``` diff --git a/lua/neotest-vstest/health.lua b/lua/neotest-vstest/health.lua new file mode 100644 index 0000000..0de7787 --- /dev/null +++ b/lua/neotest-vstest/health.lua @@ -0,0 +1,36 @@ +local M = {} +M.check = function() + vim.health.start("neotest-vstest healthcheck") + + vim.health.info("checking for dependencies...") + + local has_nio, nio = pcall(require, "nio") + if not has_nio then + vim.health.error("nio is not installed. Please install nio to use neotest-vstest.") + else + vim.health.ok("nio is installed.") + end + + local has_neotest = pcall(require, "neotest") + if not has_neotest then + vim.health.error("neotest is not installed. Please install neotest to use neotest-vstest.") + else + vim.health.ok("neotest is installed.") + end + + vim.health.info("Checking neotest-vstest configuration...") + + vim.health.info("Checking for vstest.console.dll...") + local cli_wrapper = require("neotest-vstest.vstest.cli_wrapper") + local vstest_path = cli_wrapper.get_vstest_path() + + -- make sure setup function parameters are ok + if not vstest_path then + vim.health.error( + "Could not determine location of vstest.console.dll. Please set vim.g.neotest_vstest.sdk_path to the dotnet sdk path." + ) + else + vim.health.ok("Found vstest.console.dll at: " .. vstest_path) + end +end +return M diff --git a/lua/neotest-vstest/init.lua b/lua/neotest-vstest/init.lua index af17431..69f1f06 100644 --- a/lua/neotest-vstest/init.lua +++ b/lua/neotest-vstest/init.lua @@ -6,6 +6,13 @@ ---@field settings_selector? fun(project_dir: string): string|nil function to find the .runsettings/testconfig.json in the project dir ---@field timeout_ms? number milliseconds to wait before timing out connection with test runner +---@type neotest-vstest.Config +local default_config = { + timeout_ms = 5 * 30 * 1000, +} + +vim.g.neotest_vstest = vim.tbl_deep_extend("force", default_config, vim.g.neotest_vstest or {}) + ---@param config? neotest-vstest.Config ---@return neotest.Adapter local function create_adapter(config) @@ -36,7 +43,6 @@ local function create_adapter(config) local nio = require("nio") local lib = require("neotest.lib") local logger = require("neotest.logging") - local dotnet_utils = require("neotest-vstest.dotnet_utils") if solution_dir then return solution_dir @@ -107,7 +113,6 @@ local function create_adapter(config) function DotnetNeotestAdapter.is_test_file(file_path) local logger = require("neotest.logging") - local dotnet_utils = require("neotest-vstest.dotnet_utils") local client_discovery = require("neotest-vstest.client") logger.trace("neotest-vstest: checking if file is test file: " .. file_path) @@ -145,7 +150,6 @@ local function create_adapter(config) end function DotnetNeotestAdapter.filter_dir(name, rel_path, root) - local dotnet_utils = require("neotest-vstest.dotnet_utils") local logger = require("neotest.logging") logger.trace("neotest-vstest: filtering dir", name, rel_path, root) @@ -378,7 +382,6 @@ local function create_adapter(config) local lib = require("neotest.lib") local types = require("neotest.types") local logger = require("neotest.logging") - local dotnet_utils = require("neotest-vstest.dotnet_utils") local client_discovery = require("neotest-vstest.client") logger.info(string.format("neotest-vstest: scanning %s for tests...", path)) @@ -581,9 +584,7 @@ local DotnetNeotestAdapter = create_adapter() ---@param opts neotest-vstest.Config local function apply_user_settings(_, opts) - vim.g.neotest_vstest_sdk_path = opts and opts.sdk_path or nil - vim.g.neotest_vstest_find_settings = opts and opts.settings_selector or nil - vim.g.neotest_vstest_timeout_ms = opts and opts.timeout_ms or 5 * 30 * 1000 + vim.g.neotest_vstest = vim.tbl_deep_extend("force", vim.g.neotest_vstest or {}, opts or {}) return create_adapter(opts) end diff --git a/lua/neotest-vstest/vstest/cli_wrapper.lua b/lua/neotest-vstest/vstest/cli_wrapper.lua index 1316be2..a3f55bc 100644 --- a/lua/neotest-vstest/vstest/cli_wrapper.lua +++ b/lua/neotest-vstest/vstest/cli_wrapper.lua @@ -5,16 +5,11 @@ local dotnet_utils = require("neotest-vstest.dotnet_utils") local M = {} -local function get_vstest_path() - if not vim.g.neotest_vstest_sdk_path then - local process = nio.process.run({ - cmd = "dotnet", - args = { "--info" }, - }) +function M.get_vstest_path() + local path_to_search = vim.g.neotest_vstest and vim.g.neotest_vstest.sdk_path - logger.debug( - "neotest-vstest: starting process to detect dotnet sdk path " .. tostring(process.pid) - ) + if not path_to_search then + local process = vim.system({ "dotnet", "--info" }) local default_sdk_path if vim.fn.has("win32") then @@ -23,42 +18,26 @@ local function get_vstest_path() default_sdk_path = "/usr/local/share/dotnet/sdk/" end - if not process then - vim.g.neotest_vstest_sdk_path = default_sdk_path + local obj = process:wait() + + local out = obj.stdout + local info = dotnet_utils.parse_dotnet_info(out or "") + if info.sdk_path then + path_to_search = info.sdk_path + logger.info(string.format("neotest-vstest: detected sdk path: %s", path_to_search)) + else + path_to_search = default_sdk_path local log_string = string.format( "neotest-vstest: failed to detect sdk path. falling back to %s", - vim.g.neotest_vstest_sdk_path + path_to_search ) - logger.info(log_string) nio.scheduler() vim.notify_once(log_string) - else - local out = process.stdout.read() - local info = dotnet_utils.parse_dotnet_info(out or "") - if info.sdk_path then - vim.g.neotest_vstest_sdk_path = info.sdk_path - logger.info( - string.format("neotest-vstest: detected sdk path: %s", vim.g.neotest_vstest_sdk_path) - ) - else - vim.g.neotest_vstest_sdk_path = default_sdk_path - local log_string = string.format( - "neotest-vstest: failed to detect sdk path. falling back to %s", - vim.g.neotest_vstest_sdk_path - ) - logger.info(log_string) - nio.scheduler() - vim.notify_once(log_string) - end - process.close() end end - return vim.fs.find( - "vstest.console.dll", - { upward = false, type = "file", path = vim.g.neotest_vstest_sdk_path } - )[1] + return vim.fs.find("vstest.console.dll", { upward = false, type = "file", path = path_to_search })[1] end local function get_script(script_name) @@ -76,7 +55,8 @@ end ---@return { execute: fun(content: string), stop: fun() } function M.create_test_runner(project) local test_discovery_script = get_script("run_tests.fsx") - local testhost_dll = get_vstest_path() + nio.scheduler() + local testhost_dll = M.get_vstest_path() logger.debug("neotest-vstest: found discovery script: " .. test_discovery_script) logger.debug("neotest-vstest: found testhost dll: " .. testhost_dll) diff --git a/lua/neotest-vstest/vstest/init.lua b/lua/neotest-vstest/vstest/init.lua index ffd79c8..0f16340 100644 --- a/lua/neotest-vstest/vstest/init.lua +++ b/lua/neotest-vstest/vstest/init.lua @@ -22,8 +22,8 @@ function Client:new(project) logger.info("neotest-vstest: Creating new (vstest) client for: " .. vim.inspect(project)) local findSettings = function() local settings = nil - if vim.g.neotest_vstest_find_settings then - settings = vim.g.neotest_vstest_find_settings(project.proj_dir) + if vim.g.neotest_vstest and vim.g.neotest_vstest.find_settings then + settings = vim.g.neotest_vstest.find_settings(project.proj_dir) end if settings ~= nil then return settings @@ -81,7 +81,7 @@ function Client:run_tests(ids) end nio.run(function() - cli_wrapper.spin_lock_wait_file(result_file, vim.g.neotest_vstest_timeout_ms) + cli_wrapper.spin_lock_wait_file(result_file, vim.g.neotest_vstest.timeout_ms) local parsed = {} local results = lib.files.read_lines(result_file) for _, line in ipairs(results) do @@ -130,7 +130,7 @@ function Client:debug_tests(ids) nio.run(function() local parsed = {} local file_exists = - cli_wrapper.spin_lock_wait_file(result_file, vim.g.neotest_vstest_timeout_ms) + cli_wrapper.spin_lock_wait_file(result_file, vim.g.neotest_vstest.timeout_ms) assert( file_exists, "neotest-vstest: (possible timeout, check logs) result file does not exist: " .. result_file