diff --git a/lua/neotest-dotnet/init.lua b/lua/neotest-dotnet/init.lua index d95cf07..f1ae3ba 100644 --- a/lua/neotest-dotnet/init.lua +++ b/lua/neotest-dotnet/init.lua @@ -14,10 +14,17 @@ DotnetNeotestAdapter.root = function(path) end DotnetNeotestAdapter.is_test_file = function(file_path) - return (vim.endswith(file_path, ".cs") or vim.endswith(file_path, ".fs")) - and vim.iter(vstest.discover_tests(file_path)):any(function(_, test) - return test.CodeFilePath == file_path - end) + if not (vim.endswith(file_path, ".cs") or vim.endswith(file_path, ".fs")) then + return false + else + for _, test in pairs(vstest.discover_tests(file_path)) do + if test.CodeFilePath == file_path then + return true + end + end + end + + return false end DotnetNeotestAdapter.filter_dir = function(name) @@ -70,6 +77,8 @@ local function build_position(source, captured_nodes, tests_in_file, path) end DotnetNeotestAdapter.discover_positions = function(path) + logger.info(string.format("scanning %s for tests...", path)) + local fsharp_query = require("neotest-dotnet.queries.fsharp") local c_sharp_query = require("neotest-dotnet.queries.c_sharp") @@ -142,6 +151,8 @@ DotnetNeotestAdapter.discover_positions = function(path) }) end + logger.info(string.format("done scanning %s for tests", path)) + return tree end diff --git a/lua/neotest-dotnet/vstest_wrapper.lua b/lua/neotest-dotnet/vstest_wrapper.lua index e865e26..0bab64b 100644 --- a/lua/neotest-dotnet/vstest_wrapper.lua +++ b/lua/neotest-dotnet/vstest_wrapper.lua @@ -141,26 +141,37 @@ function M.spin_lock_wait_file(file_path, max_wait) end end + if not content then + logger.warn(string.format("timed out reading content of file %s", file_path)) + end + return content end local discovery_cache = {} -local discovery_lock = nio.control.semaphore(1) +local build_semaphore = nio.control.semaphore(1) + +---@class TestCase +---@field Id string +---@field CodeFilePath string +---@field DisplayName string +---@field FullyQualifiedName string +---@field Source string ---@param path string ----@return table test_cases +---@return table test_cases function M.discover_tests(path) local json = {} local proj_info = M.get_proj_info(path) - discovery_lock.acquire() - if not proj_info.dll_file then logger.warn(string.format("failed to find dll for file: %s", path)) return json end - lib.process.run({ "dotnet", "build", proj_info.proj_file }) + build_semaphore.with(function() + lib.process.run({ "dotnet", "build", proj_info.proj_file }) + end) local open_err, stats = nio.uv.fs_stat(proj_info.dll_file) assert(not open_err, open_err) @@ -174,20 +185,9 @@ function M.discover_tests(path) and modified_time and modified_time <= cached.last_modified then - logger.debug("cache hit") - discovery_lock.release() return cached.content end - logger.debug( - string.format( - "cache not hit: %s %s %s", - proj_info.dll_file, - cached and cached.last_modified, - modified_time - ) - ) - local wait_file = nio.fn.tempname() local output_file = nio.fn.tempname() @@ -206,7 +206,7 @@ function M.discover_tests(path) invoke_test_runner(command) - logger.debug("Waiting for result file to populate...") + logger.debug(string.format("Waiting for result file to populate for %s...", proj_info.proj_file)) local max_wait = 30 * 1000 -- 10 sec @@ -224,8 +224,6 @@ function M.discover_tests(path) } end - discovery_lock.release() - return json end diff --git a/run_tests.fsx b/run_tests.fsx index 8a63bcd..9ea8179 100644 --- a/run_tests.fsx +++ b/run_tests.fsx @@ -17,6 +17,8 @@ open Microsoft.VisualStudio.TestPlatform.ObjectModel.Client open Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces module TestDiscovery = + open System.Collections.Concurrent + [] let (|DiscoveryRequest|_|) (str: string) = if str.StartsWith("discover") then @@ -61,25 +63,33 @@ module TestDiscovery = else ValueOption.None - let discoveryCompleteEvent = new ManualResetEventSlim() - - let discoveredTests = Dictionary() + let discoveredTests = ConcurrentDictionary() - type PlaygroundTestDiscoveryHandler() = + type PlaygroundTestDiscoveryHandler(resultFilePath, waitFilePath) = interface ITestDiscoveryEventsHandler2 with member _.HandleDiscoveredTests(discoveredTestCases: IEnumerable) = discoveredTestCases |> Seq.groupBy _.CodeFilePath |> Seq.iter (fun (file, testCases) -> - if discoveredTests.ContainsKey file then - discoveredTests.Remove(file) |> ignore + discoveredTests.AddOrUpdate(file, testCases, (fun _ _ -> testCases)) |> ignore) + + use testsWriter = new StreamWriter(resultFilePath, append = false) + + discoveredTests + |> _.Values + |> Seq.collect (Seq.map (fun testCase -> testCase.Id, testCase)) + |> Map + |> JsonConvert.SerializeObject + |> testsWriter.WriteLine - discoveredTests.Add(file, testCases)) + use waitFileWriter = new StreamWriter(waitFilePath, append = false) + waitFileWriter.WriteLine("1") - member _.HandleDiscoveryComplete(_, _) = discoveryCompleteEvent.Set() + Console.WriteLine($"Wrote test results to {resultFilePath}") - member _.HandleLogMessage(_, _) = () - member _.HandleRawMessage(_) = () + member _.HandleDiscoveryComplete(_, _) = () + member __.HandleLogMessage(_, _) = () + member __.HandleRawMessage(_) = () type PlaygroundTestRunHandler(streamOutputPath, outputFilePath) = interface ITestRunEventsHandler with @@ -195,7 +205,6 @@ module TestDiscovery = VsTestConsoleWrapper(console, ConsoleParameters(EnvironmentVariables = environmentVariables)) let testSession = TestSessionInfo() - let discoveryHandler = PlaygroundTestDiscoveryHandler() r.StartSession() @@ -204,23 +213,16 @@ module TestDiscovery = while loop do match Console.ReadLine() with | DiscoveryRequest args -> - discoveryCompleteEvent.Reset() - r.DiscoverTests(args.Sources, sourceSettings, options, testSession, discoveryHandler) - let _ = discoveryCompleteEvent.Wait(TimeSpan.FromSeconds(30)) - - use testsWriter = new StreamWriter(args.OutputPath, append = false) - - discoveredTests - |> _.Values - |> Seq.collect (Seq.map (fun testCase -> testCase.Id, testCase)) - |> Map - |> JsonConvert.SerializeObject - |> testsWriter.WriteLine + // spawn as task to allow running discovery + task { + do! Task.Yield() - use waitFileWriter = new StreamWriter(args.WaitFile, append = false) - waitFileWriter.WriteLine("1") + let discoveryHandler = + PlaygroundTestDiscoveryHandler(args.OutputPath, args.WaitFile) :> ITestDiscoveryEventsHandler2 - Console.WriteLine($"Wrote test results to {args.OutputPath}") + r.DiscoverTests(args.Sources, sourceSettings, options, testSession, discoveryHandler) + } + |> ignore | RunTests args -> let idMap = discoveredTests