diff --git a/Directory.Packages.props b/Directory.Packages.props index b5f1f3c..225ecdb 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,7 +3,6 @@ true true - 17.7.2 diff --git a/dotnet-bsp.sln b/dotnet-bsp.sln index 65d19cd..b4eb515 100644 --- a/dotnet-bsp.sln +++ b/dotnet-bsp.sln @@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnet-bsp", "src\bsp-serve EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "bsp-server.tests", "test\bsp-server.tests.csproj", "{4EE3A425-382F-42ED-934E-6A9676533962}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "bsp-client", "src\bsp-client\bsp-client.csproj", "{7F8753C3-6598-4773-96EA-6EB3463A3849}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -38,10 +40,15 @@ Global {4EE3A425-382F-42ED-934E-6A9676533962}.Debug|Any CPU.Build.0 = Debug|Any CPU {4EE3A425-382F-42ED-934E-6A9676533962}.Release|Any CPU.ActiveCfg = Release|Any CPU {4EE3A425-382F-42ED-934E-6A9676533962}.Release|Any CPU.Build.0 = Release|Any CPU + {7F8753C3-6598-4773-96EA-6EB3463A3849}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F8753C3-6598-4773-96EA-6EB3463A3849}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F8753C3-6598-4773-96EA-6EB3463A3849}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F8753C3-6598-4773-96EA-6EB3463A3849}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {4C290E5D-5CAD-42D7-B776-E581695D4E4A} = {992A1911-5B82-4DF3-9880-83E07F114A2D} {CC0B3E75-9BE0-466E-9723-CEBA3D4A79C6} = {992A1911-5B82-4DF3-9880-83E07F114A2D} {A88E3B50-D12B-4925-9522-E57878D2AB54} = {992A1911-5B82-4DF3-9880-83E07F114A2D} + {7F8753C3-6598-4773-96EA-6EB3463A3849} = {992A1911-5B82-4DF3-9880-83E07F114A2D} EndGlobalSection EndGlobal diff --git a/src/bsp-client/BspConnectionDetails.cs b/src/bsp-client/BspConnectionDetails.cs new file mode 100644 index 0000000..463afa2 --- /dev/null +++ b/src/bsp-client/BspConnectionDetails.cs @@ -0,0 +1,15 @@ +namespace bsp_client; + +public record BspConnectionDetails +{ + /** The name of the build tool. */ + public required string Name { get; init; } + /** The version of the build tool. */ + public required string Version { get; init; } + /** The bsp version of the build tool. */ + public required string BspVersion { get; init; } + /** A collection of languages supported by this BSP server. */ + public required string[] Languages { get; init; } + /** Command arguments runnable via system processes to start a BSP server */ + public required string[] Argv { get; init; } +} \ No newline at end of file diff --git a/test/TestHelpers/BuildServerClient.cs b/src/bsp-client/BuildServerClient.cs similarity index 86% rename from test/TestHelpers/BuildServerClient.cs rename to src/bsp-client/BuildServerClient.cs index 81cea3c..9e5318d 100644 --- a/test/TestHelpers/BuildServerClient.cs +++ b/src/bsp-client/BuildServerClient.cs @@ -1,13 +1,17 @@ using StreamJsonRpc; +using System; using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; -namespace test; +namespace bsp_client; public sealed class BuildServerClient : IDisposable { private readonly JsonRpc _jsonRpc; - public BuildServerClient(Stream sendingStream, Stream receivingStream, ServerCallbacks serverCallbacks, TraceListener? traceListener = null) + public BuildServerClient(Stream sendingStream, Stream receivingStream, IServerCallbacks serverCallbacks, TraceListener? traceListener = null) { _jsonRpc = new JsonRpc(sendingStream, receivingStream); _jsonRpc.AddLocalRpcTarget(serverCallbacks); diff --git a/test/TestHelpers/BuildServerClientTestExtensions.cs b/src/bsp-client/BuildServerClientExtensions.cs similarity index 78% rename from test/TestHelpers/BuildServerClientTestExtensions.cs rename to src/bsp-client/BuildServerClientExtensions.cs index a513c06..ead398e 100644 --- a/test/TestHelpers/BuildServerClientTestExtensions.cs +++ b/src/bsp-client/BuildServerClientExtensions.cs @@ -1,22 +1,13 @@ +using System.Threading; +using System.Threading.Tasks; using bsp4csharp.Protocol; -using dotnet_bsp; -namespace test; +namespace bsp_client; public static class BuildServerClientExtensions { - public static Task BuildInitializeAsync(this BuildServerClient client, string workspaceRootPath, CancellationToken cancellationToken) - { - var initParams = new InitializeBuildParams - { - DisplayName = "TestClient", - Version = "1.0.0", - BspVersion = "2.1.1", - RootUri = UriFixer.WithFileSchema(workspaceRootPath), - Capabilities = new BuildClientCapabilities() - }; - initParams.Capabilities.LanguageIds.Add("csharp"); - + public static Task BuildInitializeAsync(this BuildServerClient client, InitializeBuildParams initParams, CancellationToken cancellationToken) + { return client.SendRequestAsync(Methods.BuildInitialize, initParams, cancellationToken); } diff --git a/src/bsp-client/BuildServerFactory.cs b/src/bsp-client/BuildServerFactory.cs new file mode 100644 index 0000000..230b1fe --- /dev/null +++ b/src/bsp-client/BuildServerFactory.cs @@ -0,0 +1,90 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace bsp_client; + +public class BuildServerFactory +{ + private BuildServerFactory() + { + } + + public static BuildServer CreateServer(BspConnectionDetails connectionDetails, ILogger logger) + { + return new BuildServer(connectionDetails, logger); + } +} + +public sealed class BuildServer : IDisposable +{ + private readonly Stream _serverStdin; + private readonly Stream _serverStdout; + private readonly Process _process; + private readonly ILogger _logger; + + public BuildServer(BspConnectionDetails connectionDetails, ILogger logger) + { + var command = connectionDetails.Argv[0]; + var args = connectionDetails.Argv[1..]; + _process = new Process + { + StartInfo = new ProcessStartInfo(command, args) + { + CreateNoWindow = true, + RedirectStandardInput = true, + RedirectStandardOutput = true, + // RedirectStandardError = true, + } + }; + + _process.Start(); + + _serverStdin = _process.StandardInput.BaseStream; + _serverStdout = _process.StandardOutput.BaseStream; + _process.ErrorDataReceived += ErrorDataReceived; + _logger = logger; + } + + private void ErrorDataReceived(object sender, DataReceivedEventArgs e) + { + _logger.LogError(e.Data); + } + + public BuildServerClient CreateClient( + IServerCallbacks serverCallbacks, + TraceListener? traceListener = null) + { + return new BuildServerClient(_serverStdin, _serverStdout, serverCallbacks, traceListener); + } + + public Task WaitForExitAsync(CancellationToken cancellationToken) + { + return _process.WaitForExitAsync(cancellationToken); + } + + public int ExitCode => _process.ExitCode; + + public void Dispose() + { + // var errors = _process.StandardError.ReadToEnd(); + // _logger.LogError(errors); + if (!_process.HasExited) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + _process.Kill(); + } + else + { + _process.Kill(ProcessExtensions.SIGINT); + } + } + + _process.Dispose(); + } +} \ No newline at end of file diff --git a/src/bsp-client/IServerCallbacks.cs b/src/bsp-client/IServerCallbacks.cs new file mode 100644 index 0000000..9c0ac5b --- /dev/null +++ b/src/bsp-client/IServerCallbacks.cs @@ -0,0 +1,28 @@ +using bsp4csharp.Protocol; +using StreamJsonRpc; +using System.Threading.Tasks; + +namespace bsp_client; + +public interface IServerCallbacks +{ + [JsonRpcMethod(Methods.BuildPublishDiagnostics, UseSingleObjectParameterDeserialization = true)] + public Task BuildPublishDiagnosticsRecievedAsync(PublishDiagnosticsParams publishDiagnosticsParams); + + [JsonRpcMethod(Methods.BuildLogMessage, UseSingleObjectParameterDeserialization = true)] + public void BuildLogMessage(LogMessageParams logMessageParams); + + [JsonRpcMethod(Methods.BuildTaskStart, UseSingleObjectParameterDeserialization = true)] + public Task BuildTaskStartAsync(TaskStartParams taskStartParams); + + [JsonRpcMethod(Methods.BuildTaskProgress, UseSingleObjectParameterDeserialization = true)] + public Task BuildTaskProgressAsync(TaskProgressParams taskProgressParams); + + [JsonRpcMethod(Methods.BuildTaskFinish, UseSingleObjectParameterDeserialization = true)] + public Task BuildTaskFinishAsync(TaskFinishParams taskFinishParams); + + // public const string BuildShowMessage = "build/showMessage"; + // public const string BuildTargetDidChange = "buildTarget/didChange"; + // public const string RunPrintStdout = "run/printStdout"; + // public const string RunPrintStderr = "run/printStderr"; +} \ No newline at end of file diff --git a/src/bsp-client/ProcessExtensions.cs b/src/bsp-client/ProcessExtensions.cs new file mode 100644 index 0000000..963b5a8 --- /dev/null +++ b/src/bsp-client/ProcessExtensions.cs @@ -0,0 +1,17 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace bsp_client; + +public static class ProcessExtensions +{ + [DllImport ("libc", SetLastError=true, EntryPoint="kill")] + private static extern int sys_kill (int pid, int sig); + + public static int SIGINT = 2; + + public static void Kill(this Process process, int sig) + { + sys_kill(process.Id, sig); + } +} \ No newline at end of file diff --git a/src/bsp-client/bsp-client.csproj b/src/bsp-client/bsp-client.csproj new file mode 100644 index 0000000..ff5bc2e --- /dev/null +++ b/src/bsp-client/bsp-client.csproj @@ -0,0 +1,17 @@ + + + + bsp_client + + + + + + + + + + + + + diff --git a/src/bsp-client/packages.lock.json b/src/bsp-client/packages.lock.json new file mode 100644 index 0000000..99e410f --- /dev/null +++ b/src/bsp-client/packages.lock.json @@ -0,0 +1,156 @@ +{ + "version": 2, + "dependencies": { + "net8.0": { + "Microsoft.Extensions.DependencyInjection": { + "type": "Direct", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "v1SVsowG6YE1YnHVGmLWz57YTRCQRx9pH5ebIESXfm5isI9gA3QaMyg/oMTzPpXYZwSAVDzYItGJKfmV+pqXkQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Direct", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "+XTMKQyDWg4ODoNHU/BN3BaI1jhGO7VCS+BnzT/4IauiG6y2iPAte7MyD7rHKS+hNP0TkFkjrae8DFjDUxtcxg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5" + } + }, + "StreamJsonRpc": { + "type": "Direct", + "requested": "[2.22.23, )", + "resolved": "2.22.23", + "contentHash": "Ahq6uUFPnU9alny5h4agyX74th3PRq3NQCRNaDOqWcx20WT06mH/wENSk5IbHDc8BmfreQVEIBx5IXLBbsLFIA==", + "dependencies": { + "MessagePack": "2.5.192", + "Microsoft.VisualStudio.Threading.Only": "17.13.61", + "Microsoft.VisualStudio.Validation": "17.8.8", + "Nerdbank.Streams": "2.12.87", + "Newtonsoft.Json": "13.0.3", + "System.IO.Pipelines": "8.0.0" + } + }, + "MessagePack": { + "type": "Transitive", + "resolved": "2.5.192", + "contentHash": "Jtle5MaFeIFkdXtxQeL9Tu2Y3HsAQGoSntOzrn6Br/jrl6c8QmG22GEioT5HBtZJR0zw0s46OnKU8ei2M3QifA==", + "dependencies": { + "MessagePack.Annotations": "2.5.192", + "Microsoft.NET.StringTools": "17.6.3" + } + }, + "MessagePack.Annotations": { + "type": "Transitive", + "resolved": "2.5.192", + "contentHash": "jaJuwcgovWIZ8Zysdyf3b7b34/BrADw4v82GaEZymUhDd3ScMPrYd/cttekeDteJJPXseJxp04yTIcxiVUjTWg==" + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "10.0.5", + "contentHash": "iVMtq9eRvzyhx8949EGT0OCYJfXi737SbRVzWXE5GrOgGj5AaZ9eUuxA/BSUfmOMALKn/g8KfFaNQw0eiB3lyA==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "10.0.5", + "contentHash": "MDaQMdUplw0AIRhWWmbLA7yQEXaLIHb+9CTroTiNS8OlI0LMXS4LCxtopqauiqGCWlRgJ+xyraVD8t6veRAFbw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "10.0.5", + "contentHash": "/HUHJ0tw/LQvD0DZrz50eQy/3z7PfX7WWEaXnjKTV9/TNdcgFlNTZGo49QhS7PTmhDqMyHRMqAXSBxLh0vso4g==" + }, + "Microsoft.VisualStudio.Threading.Only": { + "type": "Transitive", + "resolved": "17.13.61", + "contentHash": "vl5a2URJYCO5m+aZZtNlAXAMz28e2pUotRuoHD7RnCWOCeoyd8hWp5ZBaLNYq4iEj2oeJx5ZxiSboAjVmB20Qg==", + "dependencies": { + "Microsoft.VisualStudio.Validation": "17.8.8" + } + }, + "Microsoft.VisualStudio.Validation": { + "type": "Transitive", + "resolved": "17.8.8", + "contentHash": "rWXThIpyQd4YIXghNkiv2+VLvzS+MCMKVRDR0GAMlflsdo+YcAN2g2r5U1Ah98OFjQMRexTFtXQQ2LkajxZi3g==" + }, + "Nerdbank.Streams": { + "type": "Transitive", + "resolved": "2.12.87", + "contentHash": "oDKOeKZ865I5X8qmU3IXMyrAnssYEiYWTobPGdrqubN3RtTzEHIv+D6fwhdcfrdhPJzHjCkK/ORztR/IsnmA6g==", + "dependencies": { + "Microsoft.VisualStudio.Threading.Only": "17.13.61", + "Microsoft.VisualStudio.Validation": "17.8.8", + "System.IO.Pipelines": "8.0.0" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "10.0.5", + "contentHash": "CCbzHQ26L3jskdwHh+4bxxW84lUMIrAAmeSlpO69AlrQV0DKbj1/I+feLaLSuZeqXPr9UlSy0OcgZoXOk2a6/g==" + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "FHNOatmUq0sqJOkTx+UF/9YK1f180cnW5FVqnQMvYUN0elp6wFzbtPSiqbo1/ru8ICp43JM1i7kKkk6GsNGHlA==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.6.0", + "contentHash": "OEkbBQoklHngJ8UD8ez2AERSk2g+/qpAaSWWCBFbpH727HxDq5ydVkuncBaKcKfwRqXGWx64dS6G1SUScMsitg==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.1.0", + "contentHash": "5o/HZxx6RVqYlhKSq8/zronDkALJZUT2Vz0hx43f0gwe8mwlM0y2nYlqdBwLMzr262Bwvpikeb/yEwkAa5PADg==" + }, + "bp": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[10.0.5, )", + "Microsoft.Extensions.Logging": "[10.0.5, )", + "StreamJsonRpc": "[2.22.23, )" + } + }, + "bsp4csharp": { + "type": "Project", + "dependencies": { + "bp": "[1.0.0, )" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "CentralTransitive", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "9HOdqlDtPptVcmKAjsQ/Nr5Rxfq6FMYLdhvZh1lVmeKR738qeYecQD7+ldooXf+u2KzzR1kafSphWngIM3C6ug==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "System.Diagnostics.DiagnosticSource": "10.0.5" + } + }, + "Microsoft.NET.StringTools": { + "type": "CentralTransitive", + "requested": "[18.0.2, )", + "resolved": "18.0.2", + "contentHash": "cTZw3GHkAlqZACYGeQT3niS3UfVQ8CH0O5+zUdhxstrg1Z8Q2ViXYFKjSxHmEXTX85mrOT/QnHZOeQhhSsIrkQ==", + "dependencies": { + "System.Memory": "4.6.0", + "System.Runtime.CompilerServices.Unsafe": "6.1.0" + } + } + } + } +} \ No newline at end of file diff --git a/src/bsp-server/Logging/MSBuildLogger.cs b/src/bsp-server/Logging/MSBuildLogger.cs index b22b793..a28bfbc 100644 --- a/src/bsp-server/Logging/MSBuildLogger.cs +++ b/src/bsp-server/Logging/MSBuildLogger.cs @@ -1,7 +1,6 @@ using BaseProtocol; using bsp4csharp.Protocol; using Microsoft.Build.Framework; -using Newtonsoft.Json; using Methods = bsp4csharp.Protocol.Methods; namespace dotnet_bsp.Logging; @@ -24,7 +23,7 @@ internal class MSBuildLogger(IBaseProtocolClientManager baseProtocolClientManage public LoggerVerbosity Verbosity { get; set; } = LoggerVerbosity.Normal; - public string Parameters { get; set; } = string.Empty; + public string? Parameters { get; set; } = string.Empty; public void Initialize(IEventSource eventSource) { @@ -41,10 +40,7 @@ public void Initialize(IEventSource eventSource) private void TargetStarted(object sender, TargetStartedEventArgs e) { - if (_buildStartTimestamp == null) - { - _buildStartTimestamp = _timeProvider.GetUtcNow(); - } + _buildStartTimestamp = _timeProvider.GetUtcNow(); if (_targetsOfInterest.Contains(e.TargetName, StringComparer.InvariantCultureIgnoreCase)) { diff --git a/src/bsp4csharp/Protocol/BuildClientCapabilities.cs b/src/bsp4csharp/Protocol/BuildClientCapabilities.cs index ac346e1..8518d05 100644 --- a/src/bsp4csharp/Protocol/BuildClientCapabilities.cs +++ b/src/bsp4csharp/Protocol/BuildClientCapabilities.cs @@ -12,5 +12,5 @@ public class BuildClientCapabilities /// languages than those that appear in this list. /// [DataMember(Name = "languageIds")] - public ICollection LanguageIds { get; } = new List(); + public ICollection LanguageIds { get; init; } = new List(); } \ No newline at end of file diff --git a/test/BuildServerProtocolTests.cs b/test/BuildServerProtocolTests.cs index d5261c2..f689d80 100644 --- a/test/BuildServerProtocolTests.cs +++ b/test/BuildServerProtocolTests.cs @@ -1,6 +1,7 @@ using bsp4csharp.Protocol; using dotnet_bsp; using Xunit.Abstractions; +using bsp_client; namespace test; @@ -32,7 +33,8 @@ public Task InitializeAsync() public async Task RequestWorkspaceBuildTargets_AfterInitialize_Success() { // Arrange - _ = await _client.BuildInitializeAsync(TestProjectPath.AspnetWithoutErrors, _cancellationToken); + var initParams = TestData.GetInitParams(TestProjectPath.AspnetWithoutErrors); + _ = await _client.BuildInitializeAsync(initParams, _cancellationToken); await _client.BuildInitializedAsync(); // Act @@ -68,7 +70,8 @@ public async Task RequestWorkspaceBuildTargets_AfterInitialize_Success() public async Task RequestBuildTargetCompile_ForProjectWithErrors_Success(string testProjectName, string expectedDiagnosticCode, string expectedDiagnosticMessage, int expectedDiagnosticsCount) { // Arrange - _ = await _client.BuildInitializeAsync(TestProjectPath.GetFullPathFor(testProjectName), _cancellationToken); + var initParams = TestData.GetInitParams(TestProjectPath.GetFullPathFor(testProjectName)); + _ = await _client.BuildInitializeAsync(initParams, _cancellationToken); await _client.BuildInitializedAsync(); var buildTargets = await _client.WorkspaceBuildTargetsAsync(_cancellationToken); diff --git a/test/InitializeAndInitializedTests.cs b/test/InitializeAndInitializedTests.cs index 05f93c5..061afc2 100644 --- a/test/InitializeAndInitializedTests.cs +++ b/test/InitializeAndInitializedTests.cs @@ -1,5 +1,6 @@ using StreamJsonRpc; using Xunit.Abstractions; +using bsp_client; namespace test; @@ -32,7 +33,8 @@ public async Task RequestInitializeBuild_Success() // Arrange // Act - var initResult = await _client.BuildInitializeAsync(TestProjectPath.AspnetWithoutErrors, _cancellationToken); + var initParams = TestData.GetInitParams(TestProjectPath.AspnetWithoutErrors); + var initResult = await _client.BuildInitializeAsync(initParams, _cancellationToken); // Assert Assert.Equal("dotnet-bsp", initResult.DisplayName); @@ -69,7 +71,8 @@ public async Task Requests_BeforeInitializeBuildRequest_ShouldFail() public async Task Requests_BeforeInitializedBuildNotification_ShouldFail() { // Arrange - _ = await _client.BuildInitializeAsync(TestProjectPath.AspnetWithoutErrors, _cancellationToken); + var initParams = TestData.GetInitParams(TestProjectPath.AspnetWithoutErrors); + _ = await _client.BuildInitializeAsync(initParams, _cancellationToken); // Act var act = () => _client.WorkspaceBuildTargetsAsync(_cancellationToken); diff --git a/test/ShutdownAndExitTests.cs b/test/ShutdownAndExitTests.cs index b6a9915..a1f560d 100644 --- a/test/ShutdownAndExitTests.cs +++ b/test/ShutdownAndExitTests.cs @@ -1,4 +1,5 @@ using Xunit.Abstractions; +using bsp_client; namespace test; @@ -18,7 +19,8 @@ public async Task RequestToExit_WithShutdownRequestedBefore_SuccessWithExitCodeZ cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(3)); var cancellationToken = cancellationTokenSource.Token; - _ = await client.BuildInitializeAsync(TestProjectPath.AspnetWithoutErrors, cancellationToken); + var initParams = TestData.GetInitParams(TestProjectPath.AspnetWithoutErrors); + _ = await client.BuildInitializeAsync(initParams, cancellationToken); await client.BuildInitializedAsync(); await client.ShutdownAsync(); @@ -44,7 +46,8 @@ public async Task RequestToExit_WithoutShutdownFirst_FailWithExitCodeOne() cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(3)); var cancellationToken = cancellationTokenSource.Token; - _ = await client.BuildInitializeAsync(TestProjectPath.AspnetWithoutErrors, cancellationToken); + var initParams = TestData.GetInitParams(TestProjectPath.AspnetWithoutErrors); + _ = await client.BuildInitializeAsync(initParams, cancellationToken); await client.BuildInitializedAsync(); // Act diff --git a/test/TestHelpers/BuildServerFactory.cs b/test/TestHelpers/BuildServerFactory.cs index f6dd9a4..44e8591 100644 --- a/test/TestHelpers/BuildServerFactory.cs +++ b/test/TestHelpers/BuildServerFactory.cs @@ -1,7 +1,7 @@ -using dotnet_bsp; using Microsoft.Extensions.Logging; using System.Diagnostics; using System.Runtime.InteropServices; +using bsp_client; namespace test; @@ -58,7 +58,7 @@ private void ErrorDataReceived(object sender, DataReceivedEventArgs e) _logger.LogError(e.Data); } - public BuildServerClient CreateClient(ServerCallbacks serverCallbacks, TraceListener? traceListener = null) + public BuildServerClient CreateClient(IServerCallbacks serverCallbacks, TraceListener? traceListener = null) { return new BuildServerClient(_serverStdin, _serverStdout, serverCallbacks, traceListener); } diff --git a/test/TestHelpers/ServerCallbacks.cs b/test/TestHelpers/ServerCallbacks.cs index 9eff0d0..f715d28 100644 --- a/test/TestHelpers/ServerCallbacks.cs +++ b/test/TestHelpers/ServerCallbacks.cs @@ -2,10 +2,11 @@ using StreamJsonRpc; using Xunit.Abstractions; using Microsoft.Extensions.Logging; +using bsp_client; namespace test; -public class ServerCallbacks(ITestOutputHelper testOutputHelper, LogLevel logLevel = LogLevel.Warning) +public class ServerCallbacks(ITestOutputHelper testOutputHelper, LogLevel logLevel = LogLevel.Warning) : IServerCallbacks { private readonly List _diagnosticsCollection = []; public IReadOnlyCollection Diagnostics => _diagnosticsCollection.AsReadOnly(); diff --git a/test/TestHelpers/TestProjectPath.cs b/test/TestHelpers/TestProjectPath.cs index 18d4fed..cb5ac8e 100644 --- a/test/TestHelpers/TestProjectPath.cs +++ b/test/TestHelpers/TestProjectPath.cs @@ -1,5 +1,7 @@ -namespace test; +using bsp4csharp.Protocol; +using dotnet_bsp; +namespace test; public static class TestProject { @@ -12,6 +14,24 @@ public static class TestProject public const string MsTestSlnTests = "mstest-sln-tests"; } +internal static class TestData +{ + internal static InitializeBuildParams GetInitParams(string workspaceRootPath) + { + return new InitializeBuildParams + { + DisplayName = "TestClient", + Version = "1.0.0", + BspVersion = "2.1.1", + RootUri = UriFixer.WithFileSchema(workspaceRootPath), + Capabilities = new BuildClientCapabilities + { + LanguageIds = ["csharp"] + } + }; + } +} + internal static class TestProjectPath { private static readonly string TestProjectDir = Path.GetFullPath(AppContext.BaseDirectory + "../../../../test-projects"); diff --git a/test/TestHelpers/UriFixer.cs b/test/TestHelpers/UriFixer.cs new file mode 100644 index 0000000..58dd5da --- /dev/null +++ b/test/TestHelpers/UriFixer.cs @@ -0,0 +1,10 @@ +namespace test; + +public static class UriFixer +{ + public static Uri WithFileSchema(string filePath) + { + // workaround for "file://" schema being not serialized: https://github.com/dotnet/runtime/issues/90140 + return new Uri($"file://{filePath}", UriKind.Absolute); + } +} \ No newline at end of file diff --git a/test/TestsRelatedEndpointsTests.cs b/test/TestsRelatedEndpointsTests.cs index 2c1f245..fe4d3de 100644 --- a/test/TestsRelatedEndpointsTests.cs +++ b/test/TestsRelatedEndpointsTests.cs @@ -3,6 +3,7 @@ using dotnet_bsp.Handlers; using Newtonsoft.Json.Linq; using Xunit.Abstractions; +using bsp_client; namespace test; @@ -149,7 +150,8 @@ public async Task RequestBuildTargetTestCaseDiscovery_ForFramework_Success(strin CleanupOutputDirectories(testProjectPath); - _ = await _client.BuildInitializeAsync(testProjectPath, _cancellationToken); + var initParams = TestData.GetInitParams(testProjectPath); + _ = await _client.BuildInitializeAsync(initParams, _cancellationToken); await _client.BuildInitializedAsync(); var buildTargets = await _client.WorkspaceBuildTargetsAsync(_cancellationToken); @@ -356,7 +358,8 @@ public async Task RequestBuildTargetTest_ForTestableBuildTarget_Success( CleanupOutputDirectories(testProjectPath); - _ = await _client.BuildInitializeAsync(testProjectPath, _cancellationToken); + var initParams = TestData.GetInitParams(testProjectPath); + _ = await _client.BuildInitializeAsync(initParams, _cancellationToken); await _client.BuildInitializedAsync(); // Act diff --git a/test/bsp-server.tests.csproj b/test/bsp-server.tests.csproj index 055a8c1..c61eb0f 100644 --- a/test/bsp-server.tests.csproj +++ b/test/bsp-server.tests.csproj @@ -24,6 +24,7 @@ + diff --git a/test/packages.lock.json b/test/packages.lock.json index fd949ce..b54130e 100644 --- a/test/packages.lock.json +++ b/test/packages.lock.json @@ -282,6 +282,15 @@ "StreamJsonRpc": "[2.22.23, )" } }, + "bsp-client": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[10.0.5, )", + "Microsoft.Extensions.Logging": "[10.0.5, )", + "StreamJsonRpc": "[2.22.23, )", + "bsp4csharp": "[1.0.0, )" + } + }, "bsp4csharp": { "type": "Project", "dependencies": {