Skip to content

Commit c6ded25

Browse files
authored
Add support for .runsettings files (#56) (#57)
- 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 c6ded25

File tree

6 files changed

+129
-19
lines changed

6 files changed

+129
-19
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: 5 additions & 0 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
@@ -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: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,60 @@ 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?
11-
function M.discover_tests_in_project(runner, project)
61+
function M.discover_tests_in_project(runner, settings, project)
1262
local tests_in_files = {}
1363

1464
local wait_file = nio.fn.tempname()
@@ -19,6 +69,7 @@ function M.discover_tests_in_project(runner, project)
1969
"discover",
2070
output_file,
2171
wait_file,
72+
settings or "nil",
2273
{ project.dll_file },
2374
})
2475
:flatten()
@@ -85,7 +136,7 @@ end
85136
---@param runner function
86137
---@param ids string|string[]
87138
---@return string process_output_path, string result_stream_file_path, string result_file_path
88-
function M.run_tests(runner, ids)
139+
function M.run_tests(runner, settings, ids)
89140
local process_output_path = nio.fn.tempname()
90141
lib.files.write(process_output_path, "")
91142

@@ -105,6 +156,7 @@ function M.run_tests(runner, ids)
105156
result_path,
106157
process_output_path,
107158
output_dir_path,
159+
settings or "nil",
108160
ids,
109161
})
110162
:flatten()
@@ -119,7 +171,7 @@ end
119171
---@param runner function
120172
---@param ids string|string[]
121173
---@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)
174+
function M.debug_tests(runner, settings, ids)
123175
local process_output_path = nio.fn.tempname()
124176
lib.files.write(process_output_path, "")
125177

@@ -150,6 +202,7 @@ function M.debug_tests(runner, ids)
150202
result_path,
151203
process_output_path,
152204
output_dir_path,
205+
settings or "nil",
153206
ids,
154207
})
155208
:flatten()

lua/neotest-vstest/vstest/init.lua

Lines changed: 27 additions & 6 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,20 +20,35 @@ 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 findSettings = function()
24+
local settings = nil
25+
if vim.g.neotest_vstest_find_settings then
26+
settings = vim.g.neotest_vstest_find_settings(project.proj_dir)
27+
end
28+
if settings ~= nil then
29+
return settings
30+
else
31+
return vstest_client.find_runsettings_for_project(project.proj_dir)
32+
end
33+
end
2234
local client = {
2335
project = project,
2436
test_cases = {},
2537
last_discovered = 0,
2638
test_runner = cli_wrapper.create_test_runner(project),
39+
settings = findSettings(),
2740
}
2841
setmetatable(client, self)
2942

3043
return client
3144
end
3245

3346
function Client:discover_tests()
34-
self.test_cases = vstest_client.discover_tests_in_project(self.test_runner.execute, self.project)
35-
or {}
47+
self.test_cases = vstest_client.discover_tests_in_project(
48+
self.test_runner.execute,
49+
self.settings,
50+
self.project
51+
) or {}
3652

3753
return self.test_cases
3854
end
@@ -43,7 +59,7 @@ end
4359
function Client:run_tests(ids)
4460
local result_future = nio.control.future()
4561
local process_output_file, stream_file, result_file =
46-
vstest_client.run_tests(self.test_runner.execute, ids)
62+
vstest_client.run_tests(self.test_runner.execute, self.settings, ids)
4763

4864
local result_stream_data, result_stop_stream = lib.files.stream_lines(stream_file)
4965
local output_stream_data, output_stop_stream = lib.files.stream_lines(process_output_file)
@@ -65,7 +81,7 @@ function Client:run_tests(ids)
6581
end
6682

6783
nio.run(function()
68-
cli_wrapper.spin_lock_wait_file(result_file, 5 * 30 * 1000)
84+
cli_wrapper.spin_lock_wait_file(result_file, vim.g.neotest_vstest_timeout_ms)
6985
local parsed = {}
7086
local results = lib.files.read_lines(result_file)
7187
for _, line in ipairs(results) do
@@ -90,7 +106,7 @@ end
90106
function Client:debug_tests(ids)
91107
local result_future = nio.control.future()
92108
local pid, on_attach, process_output_file, stream_file, result_file =
93-
vstest_client.debug_tests(self.test_runner.execute, ids)
109+
vstest_client.debug_tests(self.test_runner.execute, self.settings, ids)
94110

95111
local result_stream_data, result_stop_stream = lib.files.stream_lines(stream_file)
96112
local output_stream_data, output_stop_stream = lib.files.stream_lines(process_output_file)
@@ -112,8 +128,13 @@ function Client:debug_tests(ids)
112128
end
113129

114130
nio.run(function()
115-
cli_wrapper.spin_lock_wait_file(result_file, 5 * 30 * 1000)
116131
local parsed = {}
132+
local file_exists =
133+
cli_wrapper.spin_lock_wait_file(result_file, vim.g.neotest_vstest_timeout_ms)
134+
assert(
135+
file_exists,
136+
"neotest-vstest: (possible timeout, check logs) result file does not exist: " .. result_file
137+
)
117138
local results = lib.files.read_lines(result_file)
118139
for _, line in ipairs(results) do
119140
local success, result = pcall(vim.json.decode, line)

scripts/run_tests.fsx

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ module TestDiscovery =
4949

5050
{| OutputPath = args[0]
5151
WaitFile = args[1]
52-
Sources = args[2..] |}
52+
RunSettings = args[2]
53+
Sources = args[3..] |}
5354
|> ValueOption.Some
5455
else
5556
ValueOption.None
@@ -63,7 +64,8 @@ module TestDiscovery =
6364
OutputPath = args[1]
6465
ProcessOutput = args[2]
6566
OutputDirPath = args[3]
66-
Ids = args[4..] |> Array.map Guid.Parse |}
67+
RunSettings = args[4]
68+
Ids = args[5..] |> Array.map Guid.Parse |}
6769
|> ValueOption.Some
6870
else
6971
ValueOption.None
@@ -79,7 +81,8 @@ module TestDiscovery =
7981
OutputPath = args[3]
8082
ProcessOutput = args[4]
8183
OutputDirPath = args[5]
82-
Ids = args[6..] |> Array.map Guid.Parse |}
84+
RunSettings = args[6]
85+
Ids = args[7..] |> Array.map Guid.Parse |}
8386
|> ValueOption.Some
8487
else
8588
ValueOption.None
@@ -256,7 +259,7 @@ module TestDiscovery =
256259

257260
let console = argv[0]
258261

259-
let sourceSettings =
262+
let nullSettings =
260263
"""
261264
<RunSettings>
262265
</RunSettings>
@@ -294,8 +297,14 @@ module TestDiscovery =
294297
let discoveryHandler =
295298
PlaygroundTestDiscoveryHandler(args.WaitFile, args.OutputPath) :> ITestDiscoveryEventsHandler2
296299

300+
let settings =
301+
if (args.RunSettings.CompareTo "nil" <> 0) then
302+
System.IO.File.ReadAllText(args.RunSettings)
303+
else
304+
nullSettings
305+
297306
Console.WriteLine($"Discovering tests for: {sourcesStr}")
298-
r.DiscoverTests(args.Sources, sourceSettings, options, testSession, discoveryHandler)
307+
r.DiscoverTests(args.Sources, settings, options, testSession, discoveryHandler)
299308
Console.WriteLine($"Discovering tests for: {sourcesStr}")
300309
with e ->
301310
Console.WriteLine($"failed to discovery tests for {sourcesStr}. Exception: {e}")
@@ -311,8 +320,15 @@ module TestDiscovery =
311320
args.ProcessOutput,
312321
args.OutputDirPath
313322
)
323+
324+
let settings =
325+
if (args.RunSettings.CompareTo "nil" <> 0) then
326+
System.IO.File.ReadAllText(args.RunSettings)
327+
else
328+
nullSettings
329+
314330
// spawn as task to allow running concurrent tests
315-
do! r.RunTestsAsync(testCases, sourceSettings, testHandler)
331+
do! r.RunTestsAsync(testCases, settings, testHandler)
316332
Console.WriteLine($"Done running tests for ids: ")
317333

318334
for id in args.Ids do
@@ -333,11 +349,17 @@ module TestDiscovery =
333349
args.OutputDirPath
334350
)
335351

352+
let settings=
353+
if (args.RunSettings.CompareTo "nil" <> 0) then
354+
System.IO.File.ReadAllText(args.RunSettings)
355+
else
356+
nullSettings
357+
336358
let debugLauncher = DebugLauncher(args.PidPath, args.AttachedPath)
337359
Console.WriteLine($"Starting {Seq.length testCases} tests in debug-mode")
338360

339361
do! Task.Yield()
340-
r.RunTestsWithCustomTestHost(testCases, sourceSettings, testHandler, debugLauncher)
362+
r.RunTestsWithCustomTestHost(testCases, settings, testHandler, debugLauncher)
341363
}
342364
|> ignore
343365
| input ->

0 commit comments

Comments
 (0)