Skip to content

Commit 0a0d9b1

Browse files
Added lsp configuration tests (#280)
* Added lsp configuration tests * Updated test to just cover the entire call in case cancellation happens during init
1 parent 6eeee3e commit 0a0d9b1

11 files changed

+434
-14
lines changed

src/JsonRpc.Testing/JsonRpcTestBase.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public JsonRpcTestBase(JsonRpcTestOptions testOptions)
3232
protected ISettler ServerEvents { get; }
3333
protected ISettler Events { get; }
3434
protected JsonRpcTestOptions TestOptions { get; }
35-
protected CancellationToken CancellationToken => _cancellationTokenSource.Token;
35+
protected internal CancellationToken CancellationToken => _cancellationTokenSource.Token;
3636
protected Task SettleNext() => Events.SettleNext();
3737
protected IObservable<Unit> Settle() => Events.Settle();
3838

src/JsonRpc/DryIoc/DryIocAdapter.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ public static void RegisterDescriptor(this IContainer container, ServiceDescript
150150
container.RegisterMany(
151151
new [] {descriptor.ImplementationType},
152152
reuse: reuse,
153-
serviceTypeCondition: type => type == descriptor.ServiceType || typeof(IEventingHandler).IsAssignableFrom(type));
153+
serviceTypeCondition: type => type == descriptor.ImplementationType || type == descriptor.ServiceType || typeof(IEventingHandler).IsAssignableFrom(type) || typeof(IJsonRpcHandler).IsAssignableFrom(type));
154154
}
155155
else if (descriptor.ImplementationFactory != null)
156156
{
@@ -165,8 +165,9 @@ public static void RegisterDescriptor(this IContainer container, ServiceDescript
165165
else
166166
{
167167
// ensure eventing handlers are pulled in automagically.
168-
if (descriptor.ImplementationInstance is IEventingHandler)
168+
if (!(descriptor.ImplementationInstance is IEnumerable<object>) && (descriptor.ImplementationInstance is IEventingHandler || descriptor.ImplementationInstance is IJsonRpcHandler))
169169
{
170+
170171
container.RegisterInstanceMany(descriptor.ImplementationInstance);
171172
}
172173
else

src/Server/Configuration/BaseWorkspaceConfigurationProvider.cs

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ protected void ParseClientConfiguration(JToken settings, string prefix = null)
1717
// The null request (appears) to always come second
1818
// this handler is set to use the SerialAttribute
1919

20+
Data.Clear();
21+
2022
// TODO: Figure out the best way to plugin to handle additional configurations (toml, yaml?)
2123
try
2224
{

src/Server/Configuration/DidChangeConfigurationProvider.cs

+6-2
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,12 @@ public async Task<Unit> Handle(DidChangeConfigurationParams request, Cancellatio
7272
private async Task GetWorkspaceConfiguration()
7373
{
7474
var configurationItems = _configurationItems.ToArray();
75-
if (configurationItems.Length == 0) return;
76-
if (_capability == null) return;
75+
if (_capability == null || configurationItems.Length == 0)
76+
{
77+
_logger.LogWarning("No ConfigurationItems have been defined, configuration won't surface any configuration from the client!");
78+
OnReload();
79+
return;
80+
}
7781

7882
{
7983
var configurations = (await _workspaceLanguageServer.RequestConfiguration(new ConfigurationParams() {

src/Server/LanguageServerOptionsExtensions.cs

+12
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,17 @@ public static LanguageServerOptions ConfigureConfiguration(this LanguageServerOp
7272
options.ConfigurationBuilderAction = builderAction;
7373
return options;
7474
}
75+
76+
public static LanguageServerOptions WithConfigurationSection(this LanguageServerOptions options, string sectionName)
77+
{
78+
options.Services.AddSingleton(new ConfigurationItem() {Section = sectionName});
79+
return options;
80+
}
81+
82+
public static LanguageServerOptions WithConfigurationItem(this LanguageServerOptions options, ConfigurationItem configurationItem)
83+
{
84+
options.Services.AddSingleton(configurationItem);
85+
return options;
86+
}
7587
}
7688
}

src/Testing/ConfigurationHelpers.cs

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Reactive;
4+
using System.Reactive.Linq;
5+
using System.Reactive.Threading.Tasks;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.Extensions.Configuration;
9+
using Microsoft.Extensions.DependencyInjection;
10+
using Microsoft.Extensions.Options;
11+
using OmniSharp.Extensions.LanguageServer.Client;
12+
using OmniSharp.Extensions.LanguageServer.Protocol.Client;
13+
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
14+
using OmniSharp.Extensions.LanguageServer.Protocol.Workspace;
15+
using OmniSharp.Extensions.LanguageServer.Server;
16+
17+
namespace OmniSharp.Extensions.LanguageProtocol.Testing
18+
{
19+
public static class ConfigurationHelpers
20+
{
21+
public static IConfiguration ToConfiguration(this IEnumerable<KeyValuePair<string, string>> configurationData)
22+
{
23+
var cb = new ConfigurationBuilder();
24+
if (configurationData != null) cb.AddInMemoryCollection(configurationData);
25+
return cb.Build();
26+
}
27+
28+
public static Task WaitForChange(this IConfiguration configuration, CancellationToken cancellationToken)
29+
{
30+
if (configuration == null) throw new ArgumentNullException(nameof(configuration));
31+
return Observable.Create<Unit>(observer =>
32+
{
33+
var reloadToken = configuration.GetReloadToken();
34+
return reloadToken.RegisterChangeCallback(_ =>
35+
{
36+
observer.OnNext(Unit.Default);
37+
observer.OnCompleted();
38+
}, Unit.Default);
39+
}).ToTask(cancellationToken);
40+
}
41+
42+
public static Task WaitForChange<T>(this IOptionsMonitor<T> options, CancellationToken cancellationToken)
43+
{
44+
if (options == null) throw new ArgumentNullException(nameof(options));
45+
return Observable.Create<Unit>(observer =>
46+
{
47+
return options.OnChange(_ =>
48+
{
49+
observer.OnNext(Unit.Default);
50+
observer.OnCompleted();
51+
});
52+
}).ToTask(cancellationToken);
53+
}
54+
}
55+
}

src/Testing/LanguageProtocolTestBase.cs

+28-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using OmniSharp.Extensions.JsonRpc.Testing;
1111
using OmniSharp.Extensions.LanguageServer.Client;
1212
using OmniSharp.Extensions.LanguageServer.Protocol.Client;
13+
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
1314
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
1415
using OmniSharp.Extensions.LanguageServer.Server;
1516
using RealLanguageServer = OmniSharp.Extensions.LanguageServer.Server.LanguageServer;
@@ -38,7 +39,7 @@ protected virtual void ConfigureServerInputOutput(PipeReader clientOutput, PipeW
3839
options.WithInput(clientOutput).WithOutput(serverInput);
3940
}
4041

41-
protected virtual async Task<(ILanguageClient client, ILanguageServer server)> Initialize(
42+
protected internal virtual (ILanguageClient client, ILanguageServer server) Create(
4243
Action<LanguageClientOptions> clientOptionsAction,
4344
Action<LanguageServerOptions> serverOptionsAction)
4445
{
@@ -70,11 +71,37 @@ protected virtual void ConfigureServerInputOutput(PipeReader clientOutput, PipeW
7071
Disposable.Add(_client);
7172
Disposable.Add(_server);
7273

74+
return (_client, _server);
75+
}
76+
77+
protected internal virtual async Task<(ILanguageClient client, ILanguageServer server)> Initialize(
78+
Action<LanguageClientOptions> clientOptionsAction,
79+
Action<LanguageServerOptions> serverOptionsAction)
80+
{
81+
(_client, _server) = Create(clientOptionsAction, serverOptionsAction);
82+
7383
return await ObservableEx.ForkJoin(
7484
Observable.FromAsync(_client.Initialize),
7585
Observable.FromAsync(_server.Initialize),
7686
(a, b) => (_client, _server)
7787
).ToTask(CancellationToken);
7888
}
89+
90+
protected virtual async Task<(ILanguageClient client, ILanguageServer server, TestConfigurationProvider configurationProvider)> InitializeWithConfiguration(
91+
Action<LanguageClientOptions> clientOptionsAction,
92+
Action<LanguageServerOptions> serverOptionsAction
93+
) {
94+
var (client, server) = Create(options => {
95+
clientOptionsAction?.Invoke(options);
96+
options.WithCapability(new DidChangeConfigurationCapability());
97+
options.Services.AddSingleton<TestConfigurationProvider>();
98+
}, serverOptionsAction);
99+
100+
return await ObservableEx.ForkJoin(
101+
Observable.FromAsync(client.Initialize),
102+
Observable.FromAsync(server.Initialize),
103+
(a, b) => (client, server, client.GetRequiredService<TestConfigurationProvider>())
104+
).ToTask(CancellationToken);
105+
}
79106
}
80107
}

src/Testing/LanguageServerTestBase.cs

+22-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using OmniSharp.Extensions.JsonRpc.Testing;
1111
using OmniSharp.Extensions.LanguageServer.Client;
1212
using OmniSharp.Extensions.LanguageServer.Protocol.Client;
13+
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
1314
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
1415
using OmniSharp.Extensions.LanguageServer.Server;
1516

@@ -28,7 +29,7 @@ public LanguageServerTestBase(JsonRpcTestOptions jsonRpcTestOptions) : base(json
2829

2930
protected abstract (Stream clientOutput, Stream serverInput) SetupServer();
3031

31-
protected virtual async Task<ILanguageClient> InitializeClient(Action<LanguageClientOptions> clientOptionsAction = null)
32+
protected virtual ILanguageClient CreateClient(Action<LanguageClientOptions> clientOptionsAction = null)
3233
{
3334
_client = LanguageClient.PreInit(options => {
3435
var (reader, writer) = SetupServer();
@@ -45,9 +46,29 @@ protected virtual async Task<ILanguageClient> InitializeClient(Action<LanguageCl
4546

4647
Disposable.Add(_client);
4748

49+
return _client;
50+
}
51+
52+
protected virtual async Task<ILanguageClient> InitializeClient(Action<LanguageClientOptions> clientOptionsAction = null)
53+
{
54+
_client = CreateClient(clientOptionsAction);
4855
await _client.Initialize(CancellationToken);
4956

5057
return _client;
5158
}
59+
60+
protected virtual async Task<(ILanguageClient client, TestConfigurationProvider configurationProvider)> InitializeClientWithConfiguration(
61+
Action<LanguageClientOptions> clientOptionsAction = null
62+
) {
63+
var client = CreateClient(options => {
64+
clientOptionsAction?.Invoke(options);
65+
options.WithCapability(new DidChangeConfigurationCapability());
66+
options.AddHandler<TestConfigurationProvider>();
67+
});
68+
69+
await client.Initialize(CancellationToken);
70+
71+
return (client, client.GetRequiredService<TestConfigurationProvider>());
72+
}
5273
}
5374
}

0 commit comments

Comments
 (0)