Skip to content

Commit eac004e

Browse files
committed
Add support for .runsettings files (#56)
- Adding support for the (automatic) selection of a .runsettings file in a test project. - If more than one .runsettings file is present in a project directory, vim.ui.select is used to query the user for their choice. - Added a configuraiton option that allows the user to specify a function to find (or choose) a .runsettings file given a target project directory - Added an optional configuration option that allows the user to specify a timeout to use when communicating with the vstest client
1 parent 83cf9a1 commit eac004e

File tree

6 files changed

+121
-35
lines changed

6 files changed

+121
-35
lines changed

.editorconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ root = true
44
charset = utf-8
55
indent_style = space
66
indent_size = 2
7+
continuation_indent = 2
78
trim_trailing_whitespace = true
89
insert_final_newline = true

README.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,24 @@ require("neotest").setup({
7373
-- table is passed directly to DAP when debugging tests.
7474
dap_settings = {
7575
type = "netcoredbg",
76-
}
76+
},
7777
-- If multiple solutions exists the adapter will ask you to choose one.
7878
-- If you have a different heuristic for choosing a solution you can provide a function here.
7979
solution_selector = function(solutions)
8080
return nil -- return the solution you want to use or nil to let the adapter choose.
81-
end
81+
end,
82+
-- If multiple .runsettings/testconfig.json files are present in the test project directory
83+
-- you will be given the choice of file to use when setting up the adapter.
84+
-- Or you can provide a function here
85+
-- default nil to select from all files in project directory
86+
settings_selector = function(project_dir)
87+
return nil -- return the .runsettings/testconfig.json file you want to use or let the adapter choose
88+
end,
8289
build_opts = {
8390
-- Arguments that will be added to all `dotnet build` and `dotnet msbuild` commands
8491
additional_args = {}
85-
}
92+
},
93+
timeout_ms = 30 * 5 * 1000 -- number of milliseconds to wait before timeout while communicating with adapter client
8694
})
8795
}
8896
})

lua/neotest-vstest/init.lua

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
---@field build_opts? BuildOpts
44
---@field dap_settings? dap.Configuration dap settings for debugging
55
---@field solution_selector? fun(solutions: string[]): string|nil
6+
---@field settings_selector? fun(project_dir: string): string|nil function to find the .runsettings/testconfig.json in the project dir
7+
---@field timeout_ms? number milliseconds to wait before timing out connection with test runner
68

79
---@param config? neotest-vstest.Config
810
---@return neotest.Adapter
@@ -295,7 +297,7 @@ local function create_adapter(config)
295297
if not client then
296298
logger.debug(
297299
"neotest-vstest: not discovering top-level tests due to no client for project: "
298-
.. vim.inspect(project)
300+
.. vim.inspect(project)
299301
)
300302
end
301303

@@ -342,15 +344,15 @@ local function create_adapter(config)
342344
position_id = function(position, parents)
343345
return position.id
344346
or vim
345-
.iter({
346-
position.path,
347-
vim.tbl_map(function(pos)
348-
return pos.name
349-
end, parents),
350-
position.name,
351-
})
352-
:flatten()
353-
:join("::")
347+
.iter({
348+
position.path,
349+
vim.tbl_map(function(pos)
350+
return pos.name
351+
end, parents),
352+
position.name,
353+
})
354+
:flatten()
355+
:join("::")
354356
end,
355357
}))
356358

@@ -405,7 +407,7 @@ local function create_adapter(config)
405407
local query = lib.treesitter.normalise_query(
406408
filetype,
407409
filetype == "fsharp" and require("neotest-vstest.queries.fsharp")
408-
or require("neotest-vstest.queries.c_sharp")
410+
or require("neotest-vstest.queries.c_sharp")
409411
)
410412

411413
local sep = lib.files.sep
@@ -457,15 +459,15 @@ local function create_adapter(config)
457459
position_id = function(position, parents)
458460
return position.id
459461
or vim
460-
.iter({
461-
position.path,
462-
vim.tbl_map(function(pos)
463-
return pos.name
464-
end, parents),
465-
position.name,
466-
})
467-
:flatten()
468-
:join("::")
462+
.iter({
463+
position.path,
464+
vim.tbl_map(function(pos)
465+
return pos.name
466+
end, parents),
467+
position.name,
468+
})
469+
:flatten()
470+
:join("::")
469471
end,
470472
}))
471473

@@ -559,6 +561,7 @@ local function create_adapter(config)
559561

560562
return results
561563
end
564+
562565
return DotnetNeotestAdapter
563566
end
564567

@@ -567,6 +570,8 @@ local DotnetNeotestAdapter = create_adapter()
567570
---@param opts neotest-vstest.Config
568571
local function apply_user_settings(_, opts)
569572
vim.g.neotest_vstest_sdk_path = opts and opts.sdk_path or nil
573+
vim.g.neotest_vstest_find_settings = opts and opts.settings_selector or nil
574+
vim.g.neotest_vstest_timeout_ms = opts and opts.timeout_ms or 5 * 30 * 1000
570575
return create_adapter(opts)
571576
end
572577

lua/neotest-vstest/vstest/client.lua

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,56 @@ local cli_wrapper = require("neotest-vstest.vstest.cli_wrapper")
55

66
local M = {}
77

8+
---@param project_dir string
9+
---@return string? path to .runsettings file to use or nil
10+
function M.find_runsettings_for_project(project_dir)
11+
local settings = vim.fs.find(function(name, _)
12+
return name:match("%.runsettings$")
13+
end, {
14+
upward = false,
15+
type = "file",
16+
path = project_dir,
17+
limit = math.huge,
18+
})
19+
20+
for _, set in pairs(settings) do
21+
logger.debug(string.format("neotest-vstest: Found .runsettings: %s", set))
22+
end
23+
24+
local setting
25+
if #settings > 0 then
26+
local settings_future = nio.control.future()
27+
28+
if #settings == 1 then
29+
setting = settings[1]
30+
settings_future.set(setting)
31+
logger.info(string.format("neotest-vstest: selected .runsetting file: %s", setting))
32+
else
33+
vim.schedule(function()
34+
nio.run(function()
35+
vim.ui.select(settings, {
36+
prompt = "Multiple .runsettings exists. Select a .runsettings file: "
37+
}, function(selected)
38+
if selected then
39+
setting = selected
40+
logger.info(string.format("neotest-vstest: selected .runsetting file: %s", setting))
41+
settings_future.set(setting)
42+
else
43+
settings_future.set("nil")
44+
end
45+
end)
46+
end)
47+
end)
48+
end
49+
50+
if settings_future.wait() and setting then
51+
return setting
52+
end
53+
end
54+
logger.info(string.format("neotest-vstest: Found no .runsettings files"))
55+
return nil
56+
end
57+
858
---@param runner function
959
---@param project DotnetProjectInfo
1060
---@return table?
@@ -85,7 +135,7 @@ end
85135
---@param runner function
86136
---@param ids string|string[]
87137
---@return string process_output_path, string result_stream_file_path, string result_file_path
88-
function M.run_tests(runner, ids)
138+
function M.run_tests(runner, settings, ids)
89139
local process_output_path = nio.fn.tempname()
90140
lib.files.write(process_output_path, "")
91141

@@ -105,6 +155,7 @@ function M.run_tests(runner, ids)
105155
result_path,
106156
process_output_path,
107157
output_dir_path,
158+
settings or "nil",
108159
ids,
109160
})
110161
:flatten()
@@ -119,7 +170,7 @@ end
119170
---@param runner function
120171
---@param ids string|string[]
121172
---@return string? pid, async fun() on_attach, string process_output_path, string result_stream_file_path, string result_file_path
122-
function M.debug_tests(runner, ids)
173+
function M.debug_tests(runner, settings, ids)
123174
local process_output_path = nio.fn.tempname()
124175
lib.files.write(process_output_path, "")
125176

@@ -150,6 +201,7 @@ function M.debug_tests(runner, ids)
150201
result_path,
151202
process_output_path,
152203
output_dir_path,
204+
settings or "nil",
153205
ids,
154206
})
155207
:flatten()

lua/neotest-vstest/vstest/init.lua

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ local vstest_client = require("neotest-vstest.vstest.client")
77

88
--- @class neotest-vstest.vstest-client: neotest-vstest.Client
99
--- @field project DotnetProjectInfo
10+
--- @field settings string? path to .runsettings file or nil
1011
--- @field private test_runner { execute: function, stop: function }
1112
local Client = {}
1213
Client.__index = Client
@@ -19,11 +20,14 @@ end
1920
---@param project DotnetProjectInfo
2021
function Client:new(project)
2122
logger.info("neotest-vstest: Creating new (vstest) client for: " .. vim.inspect(project))
23+
local settingsSelector = vim.g.neotest_vstest_find_settings or vstest_client.find_runsettings_for_project
2224
local client = {
2325
project = project,
2426
test_cases = {},
2527
last_discovered = 0,
2628
test_runner = cli_wrapper.create_test_runner(project),
29+
settings = vim.g.neotest_vstest_find_settings and vim.g.neotest_vstest_find_settings(project.proj_dir)
30+
or settingsSelector(project.proj_dir)
2731
}
2832
setmetatable(client, self)
2933

@@ -43,7 +47,7 @@ end
4347
function Client:run_tests(ids)
4448
local result_future = nio.control.future()
4549
local process_output_file, stream_file, result_file =
46-
vstest_client.run_tests(self.test_runner.execute, ids)
50+
vstest_client.run_tests(self.test_runner.execute, self.settings, ids)
4751

4852
local result_stream_data, result_stop_stream = lib.files.stream_lines(stream_file)
4953
local output_stream_data, output_stop_stream = lib.files.stream_lines(process_output_file)
@@ -65,7 +69,7 @@ function Client:run_tests(ids)
6569
end
6670

6771
nio.run(function()
68-
cli_wrapper.spin_lock_wait_file(result_file, 5 * 30 * 1000)
72+
cli_wrapper.spin_lock_wait_file(result_file, vim.g.neotest_vstest_timeout_ms)
6973
local parsed = {}
7074
local results = lib.files.read_lines(result_file)
7175
for _, line in ipairs(results) do
@@ -90,7 +94,7 @@ end
9094
function Client:debug_tests(ids)
9195
local result_future = nio.control.future()
9296
local pid, on_attach, process_output_file, stream_file, result_file =
93-
vstest_client.debug_tests(self.test_runner.execute, ids)
97+
vstest_client.debug_tests(self.test_runner.execute, self.settings, ids)
9498

9599
local result_stream_data, result_stop_stream = lib.files.stream_lines(stream_file)
96100
local output_stream_data, output_stop_stream = lib.files.stream_lines(process_output_file)
@@ -112,8 +116,9 @@ function Client:debug_tests(ids)
112116
end
113117

114118
nio.run(function()
115-
cli_wrapper.spin_lock_wait_file(result_file, 5 * 30 * 1000)
116119
local parsed = {}
120+
local file_exists = cli_wrapper.spin_lock_wait_file(result_file, vim.g.neotest_vstest_timeout_ms)
121+
assert(file_exists, "neotest-vstest: (possible timeout, check logs) result file does not exist: " .. result_file)
117122
local results = lib.files.read_lines(result_file)
118123
for _, line in ipairs(results) do
119124
local success, result = pcall(vim.json.decode, line)

scripts/run_tests.fsx

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ module TestDiscovery =
6363
OutputPath = args[1]
6464
ProcessOutput = args[2]
6565
OutputDirPath = args[3]
66-
Ids = args[4..] |> Array.map Guid.Parse |}
66+
RunSettings = args[4]
67+
Ids = args[5..] |> Array.map Guid.Parse |}
6768
|> ValueOption.Some
6869
else
6970
ValueOption.None
@@ -79,7 +80,8 @@ module TestDiscovery =
7980
OutputPath = args[3]
8081
ProcessOutput = args[4]
8182
OutputDirPath = args[5]
82-
Ids = args[6..] |> Array.map Guid.Parse |}
83+
RunSettings = args[6]
84+
Ids = args[7..] |> Array.map Guid.Parse |}
8385
|> ValueOption.Some
8486
else
8587
ValueOption.None
@@ -256,7 +258,7 @@ module TestDiscovery =
256258

257259
let console = argv[0]
258260

259-
let sourceSettings =
261+
let nullSettings =
260262
"""
261263
<RunSettings>
262264
</RunSettings>
@@ -295,7 +297,7 @@ module TestDiscovery =
295297
PlaygroundTestDiscoveryHandler(args.WaitFile, args.OutputPath) :> ITestDiscoveryEventsHandler2
296298

297299
Console.WriteLine($"Discovering tests for: {sourcesStr}")
298-
r.DiscoverTests(args.Sources, sourceSettings, options, testSession, discoveryHandler)
300+
r.DiscoverTests(args.Sources, nullSettings, options, testSession, discoveryHandler)
299301
Console.WriteLine($"Discovering tests for: {sourcesStr}")
300302
with e ->
301303
Console.WriteLine($"failed to discovery tests for {sourcesStr}. Exception: {e}")
@@ -311,8 +313,15 @@ module TestDiscovery =
311313
args.ProcessOutput,
312314
args.OutputDirPath
313315
)
316+
317+
let settings =
318+
if (args.RunSettings.CompareTo "nil" <> 0) then
319+
System.IO.File.ReadAllText(args.RunSettings)
320+
else
321+
nullSettings
322+
314323
// spawn as task to allow running concurrent tests
315-
do! r.RunTestsAsync(testCases, sourceSettings, testHandler)
324+
do! r.RunTestsAsync(testCases, settings, testHandler)
316325
Console.WriteLine($"Done running tests for ids: ")
317326

318327
for id in args.Ids do
@@ -333,11 +342,17 @@ module TestDiscovery =
333342
args.OutputDirPath
334343
)
335344

345+
let settings=
346+
if (args.RunSettings.CompareTo "nil" <> 0) then
347+
System.IO.File.ReadAllText(args.RunSettings)
348+
else
349+
nullSettings
350+
336351
let debugLauncher = DebugLauncher(args.PidPath, args.AttachedPath)
337352
Console.WriteLine($"Starting {Seq.length testCases} tests in debug-mode")
338353

339354
do! Task.Yield()
340-
r.RunTestsWithCustomTestHost(testCases, sourceSettings, testHandler, debugLauncher)
355+
r.RunTestsWithCustomTestHost(testCases, settings, testHandler, debugLauncher)
341356
}
342357
|> ignore
343358
| input ->

0 commit comments

Comments
 (0)