diff --git a/src/Client/Dispatcher/LspDispatcher.cs b/src/Client/Dispatcher/LspDispatcher.cs index c4a90fc04..e2b6c546f 100644 --- a/src/Client/Dispatcher/LspDispatcher.cs +++ b/src/Client/Dispatcher/LspDispatcher.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json.Linq; +using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.LanguageServer.Client.Handlers; namespace OmniSharp.Extensions.LanguageServer.Client.Dispatcher @@ -21,10 +22,22 @@ public class LspDispatcher /// /// Create a new . /// - public LspDispatcher() + /// + /// The JSON serialiser for notification / request / response payloads. + /// + public LspDispatcher(ISerializer serializer) { + if (serializer == null) + throw new ArgumentNullException(nameof(serializer)); + + Serializer = serializer; } + /// + /// The JSON serialiser to use for notification / request / response payloads. + /// + public ISerializer Serializer { get; set; } + /// /// Register a handler invoker. /// @@ -92,7 +105,9 @@ public async Task TryHandleNotification(string method, JObject notificatio if (_handlers.TryGetValue(method, out IHandler handler) && handler is IInvokeNotificationHandler notificationHandler) { - await notificationHandler.Invoke(notification); + object notificationPayload = DeserializePayload(notificationHandler.PayloadType, notification); + + await notificationHandler.Invoke(notificationPayload); return true; } @@ -121,9 +136,36 @@ public Task TryHandleRequest(string method, JObject request, Cancellatio throw new ArgumentException($"Argument cannot be null, empty, or entirely composed of whitespace: {nameof(method)}.", nameof(method)); if (_handlers.TryGetValue(method, out IHandler handler) && handler is IInvokeRequestHandler requestHandler) - return requestHandler.Invoke(request, cancellationToken); + { + object requestPayload = DeserializePayload(requestHandler.PayloadType, request); + + return requestHandler.Invoke(requestPayload, cancellationToken); + } return null; } + + /// + /// Deserialise a notification / request payload from JSON. + /// + /// + /// The payload's CLR type. + /// + /// + /// JSON representing the payload. + /// + /// + /// The deserialised payload (if one is present and expected). + /// + object DeserializePayload(Type payloadType, JObject payload) + { + if (payloadType == null) + throw new ArgumentNullException(nameof(payloadType)); + + if (payloadType == null || payload == null) + return null; + + return payload.ToObject(payloadType, Serializer.JsonSerializer); + } } } diff --git a/src/Client/Handlers/DelegateEmptyNotificationHandler.cs b/src/Client/Handlers/DelegateEmptyNotificationHandler.cs index a0d164d44..5b8ef95a0 100644 --- a/src/Client/Handlers/DelegateEmptyNotificationHandler.cs +++ b/src/Client/Handlers/DelegateEmptyNotificationHandler.cs @@ -32,6 +32,11 @@ public DelegateEmptyNotificationHandler(string method, NotificationHandler handl /// public NotificationHandler Handler { get; } + /// + /// The expected CLR type of the notification payload (null, since the handler does not use the request payload). + /// + public override Type PayloadType => null; + /// /// Invoke the handler. /// diff --git a/src/Client/Handlers/DelegateHandler.cs b/src/Client/Handlers/DelegateHandler.cs index 3813e4288..73c31a21a 100644 --- a/src/Client/Handlers/DelegateHandler.cs +++ b/src/Client/Handlers/DelegateHandler.cs @@ -26,5 +26,10 @@ protected DelegateHandler(string method) /// The name of the method handled by the handler. /// public string Method { get; } + + /// + /// The expected CLR type of the request / notification payload (if any; null if the handler does not use the request payload). + /// + public abstract Type PayloadType { get; } } } diff --git a/src/Client/Handlers/DelegateNotificationHandler.cs b/src/Client/Handlers/DelegateNotificationHandler.cs index 953ae4ef7..5e5698a72 100644 --- a/src/Client/Handlers/DelegateNotificationHandler.cs +++ b/src/Client/Handlers/DelegateNotificationHandler.cs @@ -38,6 +38,11 @@ public DelegateNotificationHandler(string method, NotificationHandler public NotificationHandler Handler { get; } + /// + /// The expected CLR type of the notification payload. + /// + public override Type PayloadType => typeof(TNotification); + /// /// Invoke the handler. /// @@ -47,12 +52,12 @@ public DelegateNotificationHandler(string method, NotificationHandler /// A representing the operation. /// - public async Task Invoke(JObject notification) + public async Task Invoke(object notification) { await Task.Yield(); Handler( - notification != null ? notification.ToObject(Serializer.Instance.JsonSerializer /* Fix me: this is ugly */) : default(TNotification) + (TNotification)notification ); } } diff --git a/src/Client/Handlers/DelegateRequestHandler.cs b/src/Client/Handlers/DelegateRequestHandler.cs index 706e2c5c3..9ae07cebc 100644 --- a/src/Client/Handlers/DelegateRequestHandler.cs +++ b/src/Client/Handlers/DelegateRequestHandler.cs @@ -39,6 +39,11 @@ public DelegateRequestHandler(string method, RequestHandler handler) /// public RequestHandler Handler { get; } + /// + /// The expected CLR type of the request payload. + /// + public override Type PayloadType => typeof(TRequest); + /// /// Invoke the handler. /// @@ -51,10 +56,10 @@ public DelegateRequestHandler(string method, RequestHandler handler) /// /// A representing the operation. /// - public async Task Invoke(JObject request, CancellationToken cancellationToken) + public async Task Invoke(object request, CancellationToken cancellationToken) { await Handler( - request != null ? request.ToObject(Serializer.Instance.JsonSerializer /* Fix me: this is ugly */) : default(TRequest), + (TRequest)request, cancellationToken ); diff --git a/src/Client/Handlers/DelegateRequestResponseHandler.cs b/src/Client/Handlers/DelegateRequestResponseHandler.cs index 43095e74b..4975a253b 100644 --- a/src/Client/Handlers/DelegateRequestResponseHandler.cs +++ b/src/Client/Handlers/DelegateRequestResponseHandler.cs @@ -42,6 +42,11 @@ public DelegateRequestResponseHandler(string method, RequestHandler public RequestHandler Handler { get; } + /// + /// The expected CLR type of the request payload. + /// + public override Type PayloadType => typeof(TRequest); + /// /// Invoke the handler. /// @@ -54,10 +59,10 @@ public DelegateRequestResponseHandler(string method, RequestHandler /// A representing the operation. /// - public async Task Invoke(JObject request, CancellationToken cancellationToken) + public async Task Invoke(object request, CancellationToken cancellationToken) { return await Handler( - request != null ? request.ToObject(Serializer.Instance.JsonSerializer /* Fix me: this is ugly */) : default(TRequest), + (TRequest)request, cancellationToken ); } diff --git a/src/Client/Handlers/DynamicRegistrationHandler.cs b/src/Client/Handlers/DynamicRegistrationHandler.cs index 188f135b9..9a371a1b4 100644 --- a/src/Client/Handlers/DynamicRegistrationHandler.cs +++ b/src/Client/Handlers/DynamicRegistrationHandler.cs @@ -1,3 +1,4 @@ +using System; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json.Linq; @@ -31,6 +32,11 @@ public DynamicRegistrationHandler() /// public string Method => "client/registerCapability"; + /// + /// The expected CLR type of the request / notification payload (if any; null if the handler does not use the request payload). + /// + public Type PayloadType => null; + /// /// Invoke the handler. /// @@ -43,7 +49,7 @@ public DynamicRegistrationHandler() /// /// A representing the operation. /// - public Task Invoke(JObject request, CancellationToken cancellationToken) + public Task Invoke(object request, CancellationToken cancellationToken) { // For now, we don't really support dynamic registration but OmniSharp's implementation sends a request even when dynamic registrations are not supported. diff --git a/src/Client/Handlers/IHandler.cs b/src/Client/Handlers/IHandler.cs index c206c4ab3..90e523a36 100644 --- a/src/Client/Handlers/IHandler.cs +++ b/src/Client/Handlers/IHandler.cs @@ -1,4 +1,6 @@ -namespace OmniSharp.Extensions.LanguageServer.Client.Handlers +using System; + +namespace OmniSharp.Extensions.LanguageServer.Client.Handlers { /// /// Represents a client-side message handler. @@ -9,5 +11,10 @@ public interface IHandler /// The name of the method handled by the handler. /// string Method { get; } + + /// + /// The expected CLR type of the request / notification payload (if any; null if the handler does not use the request body). + /// + Type PayloadType { get; } } } diff --git a/src/Client/Handlers/IInvokeNotificationHandler.cs b/src/Client/Handlers/IInvokeNotificationHandler.cs index e6aaedcf3..00a66de3d 100644 --- a/src/Client/Handlers/IInvokeNotificationHandler.cs +++ b/src/Client/Handlers/IInvokeNotificationHandler.cs @@ -18,6 +18,6 @@ public interface IInvokeNotificationHandler /// /// A representing the operation. /// - Task Invoke(JObject notification); + Task Invoke(object notification); } } diff --git a/src/Client/Handlers/IInvokeRequestHandler.cs b/src/Client/Handlers/IInvokeRequestHandler.cs index 480ceaa18..be7d279c4 100644 --- a/src/Client/Handlers/IInvokeRequestHandler.cs +++ b/src/Client/Handlers/IInvokeRequestHandler.cs @@ -22,6 +22,6 @@ public interface IInvokeRequestHandler /// /// A representing the operation. /// - Task Invoke(JObject request, CancellationToken cancellationToken); + Task Invoke(object request, CancellationToken cancellationToken); } } diff --git a/src/Client/Handlers/JsonRpcEmptyNotificationHandler.cs b/src/Client/Handlers/JsonRpcEmptyNotificationHandler.cs index b3c86f5eb..94a68d378 100644 --- a/src/Client/Handlers/JsonRpcEmptyNotificationHandler.cs +++ b/src/Client/Handlers/JsonRpcEmptyNotificationHandler.cs @@ -33,6 +33,11 @@ public JsonRpcEmptyNotificationHandler(string method, INotificationHandler handl /// public INotificationHandler Handler { get; } + /// + /// The expected CLR type of the notification payload (null, since the handler does not use the request payload). + /// + public override Type PayloadType => null; + /// /// Invoke the handler. /// diff --git a/src/Client/Handlers/JsonRpcHandler.cs b/src/Client/Handlers/JsonRpcHandler.cs index c49d7a592..770d38fce 100644 --- a/src/Client/Handlers/JsonRpcHandler.cs +++ b/src/Client/Handlers/JsonRpcHandler.cs @@ -27,5 +27,10 @@ protected JsonRpcHandler(string method) /// The name of the method handled by the handler. /// public string Method { get; } + + /// + /// The expected CLR type of the request / notification payload (if any; null if the handler does not use the request payload). + /// + public abstract Type PayloadType { get; } } } diff --git a/src/Client/Handlers/JsonRpcNotificationHandler.cs b/src/Client/Handlers/JsonRpcNotificationHandler.cs index ea83b5eaf..372174eb5 100644 --- a/src/Client/Handlers/JsonRpcNotificationHandler.cs +++ b/src/Client/Handlers/JsonRpcNotificationHandler.cs @@ -1,6 +1,5 @@ using System; using System.Threading.Tasks; -using Newtonsoft.Json.Linq; using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.LanguageServer.Protocol; using OmniSharp.Extensions.LanguageServer.Protocol.Serialization; @@ -39,6 +38,11 @@ public JsonRpcNotificationHandler(string method, INotificationHandler public INotificationHandler Handler { get; } + /// + /// The expected CLR type of the notification payload. + /// + public override Type PayloadType => typeof(TNotification); + /// /// Invoke the handler. /// @@ -48,8 +52,8 @@ public JsonRpcNotificationHandler(string method, INotificationHandler /// A representing the operation. /// - public Task Invoke(JObject notification) => Handler.Handle( - notification != null ? notification.ToObject(Serializer.Instance.JsonSerializer /* Fix me: this is ugly */) : default(TNotification) + public Task Invoke(object notification) => Handler.Handle( + (TNotification)notification ); } } diff --git a/src/Client/LanguageClient.cs b/src/Client/LanguageClient.cs index b5e9aab78..17a5b5b3c 100644 --- a/src/Client/LanguageClient.cs +++ b/src/Client/LanguageClient.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.LanguageServer.Client.Clients; using OmniSharp.Extensions.LanguageServer.Client.Dispatcher; using OmniSharp.Extensions.LanguageServer.Client.Handlers; @@ -10,6 +11,7 @@ using OmniSharp.Extensions.LanguageServer.Client.Protocol; using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Serialization; using OmniSharp.Extensions.LanguageServer.Protocol.Server.Capabilities; namespace OmniSharp.Extensions.LanguageServer.Client @@ -23,10 +25,18 @@ namespace OmniSharp.Extensions.LanguageServer.Client public sealed class LanguageClient : IDisposable { + /// + /// The serialiser for notification / request / response bodies. + /// + /// + /// TODO: Make this injectable. And what does client version do - do we have to negotiate this? + /// + readonly ISerializer _serializer = new Serializer(ClientVersion.Lsp3); + /// /// The dispatcher for incoming requests, notifications, and responses. /// - readonly LspDispatcher _dispatcher = new LspDispatcher(); + readonly LspDispatcher _dispatcher; /// /// The handler for dynamic registration of server capabilities. @@ -101,6 +111,7 @@ public LanguageClient(ILoggerFactory loggerFactory, ServerProcess process) Window = new WindowClient(this); TextDocument = new TextDocumentClient(this); + _dispatcher = new LspDispatcher(_serializer); _dispatcher.RegisterHandler(_dynamicRegistrationHandler); } diff --git a/src/Client/Protocol/LspConnection.cs b/src/Client/Protocol/LspConnection.cs index 0731c6d65..b605d97a6 100644 --- a/src/Client/Protocol/LspConnection.cs +++ b/src/Client/Protocol/LspConnection.cs @@ -126,8 +126,6 @@ public sealed class LspConnection /// Task _dispatchLoop; - private readonly Serializer _serializer; - /// /// Create a new . /// @@ -160,8 +158,10 @@ public LspConnection(ILoggerFactory loggerFactory, Stream input, Stream output) Log = loggerFactory.CreateLogger(); _input = input; _output = output; - // What does client version do? Do we have to negotaite this? - _serializer = new Serializer(ClientVersion.Lsp3); + + // What does client version do? Do we have to negotiate this? + // The connection may change its Serializer instance once connected; this can be propagated to other components as required. + Serializer = new Serializer(ClientVersion.Lsp3); } /// @@ -189,6 +189,11 @@ public void Dispose() /// ILogger Log { get; } + /// + /// The JSON serializer used for notification, request, and response payloads. + /// + public Serializer Serializer { get; } + /// /// Is the connection open? /// @@ -238,6 +243,7 @@ public void Connect(LspDispatcher dispatcher) _cancellation = _cancellationSource.Token; _dispatcher = dispatcher; + _dispatcher.Serializer = Serializer; _sendLoop = SendLoop(); _receiveLoop = ReceiveLoop(); _dispatchLoop = DispatchLoop(); @@ -337,7 +343,7 @@ public void SendNotification(string method, object notification) { // No Id means it's a notification. Method = method, - Params = JObject.FromObject(notification, _serializer.JsonSerializer) + Params = JObject.FromObject(notification, Serializer.JsonSerializer) }); } @@ -395,7 +401,7 @@ public void SendNotification(string method, object notification) { Id = requestId, Method = method, - Params = request != null ? JObject.FromObject(request, _serializer.JsonSerializer) : null + Params = request != null ? JObject.FromObject(request, Serializer.JsonSerializer) : null }); await responseCompletion.Task; @@ -458,13 +464,13 @@ public void SendNotification(string method, object notification) { Id = requestId, Method = method, - Params = request != null ? JObject.FromObject(request, _serializer.JsonSerializer) : null + Params = request != null ? JObject.FromObject(request, Serializer.JsonSerializer) : null }); ServerMessage response = await responseCompletion.Task; if (response.Result != null) - return response.Result.ToObject(_serializer.JsonSerializer); + return response.Result.ToObject(Serializer.JsonSerializer); else return default(TResponse); } @@ -660,7 +666,7 @@ async Task SendMessage(TMessage message) if (message == null) throw new ArgumentNullException(nameof(message)); - string payload = JsonConvert.SerializeObject(message, _serializer.Settings); + string payload = JsonConvert.SerializeObject(message, Serializer.Settings); byte[] payloadBuffer = PayloadEncoding.GetBytes(payload); byte[] headerBuffer = HeaderEncoding.GetBytes( @@ -760,7 +766,7 @@ async Task ReceiveMessage() Log.LogDebug("Received entire payload ({ReceivedByteCount} bytes).", received); string responseBody = PayloadEncoding.GetString(requestBuffer); - ServerMessage message = JsonConvert.DeserializeObject(responseBody, _serializer.Settings); + ServerMessage message = JsonConvert.DeserializeObject(responseBody, Serializer.Settings); Log.LogDebug("Read response body {ResponseBody}.", responseBody); @@ -893,7 +899,7 @@ private void DispatchRequest(ServerMessage requestMessage) { Id = requestMessage.Id, Method = requestMessage.Method, - Result = handlerTask.Result != null ? JObject.FromObject(handlerTask.Result, _serializer.JsonSerializer) : null + Result = handlerTask.Result != null ? JObject.FromObject(handlerTask.Result, Serializer.JsonSerializer) : null }); } diff --git a/test/Client.Tests/ClientTests.cs b/test/Client.Tests/ClientTests.cs index 4e056bc6e..3778c6714 100644 --- a/test/Client.Tests/ClientTests.cs +++ b/test/Client.Tests/ClientTests.cs @@ -13,6 +13,8 @@ using OmniSharp.Extensions.LanguageServer.Protocol.Models; using OmniSharp.Extensions.LanguageServer.Protocol.Server.Capabilities; using OmniSharp.Extensions.LanguageServer.Protocol; +using OmniSharp.Extensions.LanguageServer.Protocol.Serialization; +using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; namespace OmniSharp.Extensions.LanguageServerProtocol.Client.Tests { @@ -46,7 +48,7 @@ public ClientTests(ITestOutputHelper testOutput) /// /// The server-side dispatcher. /// - LspDispatcher ServerDispatcher { get; } = new LspDispatcher(); + LspDispatcher ServerDispatcher { get; } = new LspDispatcher(new Serializer(ClientVersion.Lsp3)); /// /// The server-side connection. diff --git a/test/Client.Tests/ConnectionTests.cs b/test/Client.Tests/ConnectionTests.cs index 4ae947823..bd9c9b923 100644 --- a/test/Client.Tests/ConnectionTests.cs +++ b/test/Client.Tests/ConnectionTests.cs @@ -4,6 +4,8 @@ using OmniSharp.Extensions.LanguageServer.Client.Protocol; using Xunit; using Xunit.Abstractions; +using OmniSharp.Extensions.LanguageServer.Protocol.Serialization; +using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; namespace OmniSharp.Extensions.LanguageServerProtocol.Client.Tests { @@ -35,7 +37,7 @@ public async Task Client_HandleEmptyNotification_Success() LspConnection clientConnection = await CreateClientConnection(); LspConnection serverConnection = await CreateServerConnection(); - var serverDispatcher = new LspDispatcher(); + var serverDispatcher = CreateDispatcher(); serverDispatcher.HandleEmptyNotification("test", () => { Log.LogInformation("Got notification."); @@ -44,7 +46,7 @@ public async Task Client_HandleEmptyNotification_Success() }); serverConnection.Connect(serverDispatcher); - clientConnection.Connect(new LspDispatcher()); + clientConnection.Connect(CreateDispatcher()); clientConnection.SendEmptyNotification("test"); await testCompletion.Task; @@ -66,7 +68,7 @@ public async Task Server_HandleEmptyNotification_Success() LspConnection clientConnection = await CreateClientConnection(); LspConnection serverConnection = await CreateServerConnection(); - var clientDispatcher = new LspDispatcher(); + var clientDispatcher = CreateDispatcher(); clientDispatcher.HandleEmptyNotification("test", () => { Log.LogInformation("Got notification."); @@ -75,7 +77,7 @@ public async Task Server_HandleEmptyNotification_Success() }); clientConnection.Connect(clientDispatcher); - serverConnection.Connect(new LspDispatcher()); + serverConnection.Connect(CreateDispatcher()); serverConnection.SendEmptyNotification("test"); await testCompletion.Task; @@ -95,7 +97,7 @@ public async Task Server_HandleRequest_Success() LspConnection clientConnection = await CreateClientConnection(); LspConnection serverConnection = await CreateServerConnection(); - var clientDispatcher = new LspDispatcher(); + var clientDispatcher = CreateDispatcher(); clientDispatcher.HandleRequest("test", (request, cancellationToken) => { Log.LogInformation("Got request: {@Request}", request); @@ -107,7 +109,7 @@ public async Task Server_HandleRequest_Success() }); clientConnection.Connect(clientDispatcher); - serverConnection.Connect(new LspDispatcher()); + serverConnection.Connect(CreateDispatcher()); TestResponse response = await serverConnection.SendRequest("test", new TestRequest { Value = 1234 @@ -132,7 +134,7 @@ public async Task Client_HandleRequest_Success() LspConnection clientConnection = await CreateClientConnection(); LspConnection serverConnection = await CreateServerConnection(); - var serverDispatcher = new LspDispatcher(); + var serverDispatcher = CreateDispatcher(); serverDispatcher.HandleRequest("test", (request, cancellationToken) => { Log.LogInformation("Got request: {@Request}", request); @@ -144,7 +146,7 @@ public async Task Client_HandleRequest_Success() }); serverConnection.Connect(serverDispatcher); - clientConnection.Connect(new LspDispatcher()); + clientConnection.Connect(CreateDispatcher()); TestResponse response = await clientConnection.SendRequest("test", new TestRequest { Value = 1234 @@ -169,7 +171,7 @@ public async Task Server_HandleCommandRequest_Success() LspConnection clientConnection = await CreateClientConnection(); LspConnection serverConnection = await CreateServerConnection(); - var clientDispatcher = new LspDispatcher(); + var clientDispatcher = CreateDispatcher(); clientDispatcher.HandleRequest("test", (request, cancellationToken) => { Log.LogInformation("Got request: {@Request}", request); @@ -180,7 +182,7 @@ public async Task Server_HandleCommandRequest_Success() }); clientConnection.Connect(clientDispatcher); - serverConnection.Connect(new LspDispatcher()); + serverConnection.Connect(CreateDispatcher()); await serverConnection.SendRequest("test", new TestRequest { Value = 1234 @@ -201,7 +203,7 @@ public async Task Client_HandleCommandRequest_Success() LspConnection clientConnection = await CreateClientConnection(); LspConnection serverConnection = await CreateServerConnection(); - var serverDispatcher = new LspDispatcher(); + var serverDispatcher = CreateDispatcher(); serverDispatcher.HandleRequest("test", (request, cancellationToken) => { Log.LogInformation("Got request: {@Request}", request); @@ -212,7 +214,7 @@ public async Task Client_HandleCommandRequest_Success() }); serverConnection.Connect(serverDispatcher); - clientConnection.Connect(new LspDispatcher()); + clientConnection.Connect(CreateDispatcher()); await clientConnection.SendRequest("test", new TestRequest { Value = 1234 @@ -223,5 +225,13 @@ public async Task Client_HandleCommandRequest_Success() await Task.WhenAll(clientConnection.HasHasDisconnected, serverConnection.HasHasDisconnected); } + + /// + /// Create an for use in tests. + /// + /// + /// The . + /// + LspDispatcher CreateDispatcher() => new LspDispatcher(new Serializer(ClientVersion.Lsp3)); } } diff --git a/test/Client.Tests/HandlerTests.cs b/test/Client.Tests/HandlerTests.cs new file mode 100644 index 000000000..f93c7a102 --- /dev/null +++ b/test/Client.Tests/HandlerTests.cs @@ -0,0 +1,97 @@ +using OmniSharp.Extensions.LanguageServer.Client.Handlers; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +namespace OmniSharp.Extensions.LanguageServerProtocol.Client.Tests +{ + /// + /// Tests for and friends. + /// + public class HandlerTests + : TestBase + { + /// + /// Create a new test suite. + /// + /// + /// Output for the current test. + /// + public HandlerTests(ITestOutputHelper testOutput) + : base(testOutput) + { + } + + /// + /// Verify that specifies the correct payload type. + /// + [Fact(DisplayName = "DelegateEmptyNotificationHandler specifies correct payload type")] + public void DelegateEmptyNotificationHandler_PayloadType() + { + IHandler handler = new DelegateEmptyNotificationHandler( + method: "test", + handler: () => + { + // Nothing to do. + } + ); + + Assert.Null(handler.PayloadType); + } + + /// + /// Verify that specifies the correct payload type. + /// + [Fact(DisplayName = "DelegateNotificationHandler specifies correct payload type")] + public void DelegateNotificationHandler_PayloadType() + { + IHandler handler = new DelegateNotificationHandler( + method: "test", + handler: notification => + { + // Nothing to do. + } + ); + + Assert.Equal(typeof(string), handler.PayloadType); + } + + /// + /// Verify that specifies the correct payload type (null). + /// + [Fact(DisplayName = "DelegateRequestHandler specifies correct payload type")] + public void DelegateRequestHandler_PayloadType() + { + IHandler handler = new DelegateRequestHandler( + method: "test", + handler: (request, cancellationToken) => + { + // Nothing to do. + + return Task.CompletedTask; + } + ); + + Assert.Equal(typeof(string), handler.PayloadType); + } + + /// + /// Verify that specifies the correct payload type (null). + /// + [Fact(DisplayName = "DelegateRequestResponseHandler specifies correct payload type")] + public void DelegateRequestResponseHandler_PayloadType() + { + IHandler handler = new DelegateRequestResponseHandler( + method: "test", + handler: (request, cancellationToken) => + { + // Nothing to do. + + return Task.FromResult("hello"); + } + ); + + Assert.Equal(typeof(string), handler.PayloadType); + } + } +}