diff --git a/Directory.Build.targets b/Directory.Build.targets index 46eeba140..42ef6cfdb 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,52 +1,53 @@ - - - - $(BaseIntermediateOutputPath)\GeneratedFiles - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + $(BaseIntermediateOutputPath)\GeneratedFiles + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Client/LanguageClient.cs b/src/Client/LanguageClient.cs index e2304fb25..0d5ba8b19 100644 --- a/src/Client/LanguageClient.cs +++ b/src/Client/LanguageClient.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Reactive.Concurrency; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reactive.Subjects; @@ -48,6 +49,7 @@ public class LanguageClient : JsonRpcServerBase, ILanguageClient private readonly IEnumerable _initializedHandlers; private readonly LspSerializer _serializer; private readonly InstanceHasStarted _instanceHasStarted; + private readonly IScheduler _scheduler; private readonly IResponseRouter _responseRouter; private readonly ISubject _initializeComplete = new AsyncSubject(); private readonly CompositeDisposable _disposable = new CompositeDisposable(); @@ -150,7 +152,8 @@ internal LanguageClient( IEnumerable initializedDelegates, IEnumerable initializedHandlers, LspSerializer serializer, - InstanceHasStarted instanceHasStarted + InstanceHasStarted instanceHasStarted, + IScheduler scheduler ) : base(handlerCollection, responseRouter) { _connection = connection; @@ -179,6 +182,7 @@ InstanceHasStarted instanceHasStarted _initializedHandlers = initializedHandlers; _serializer = serializer; _instanceHasStarted = instanceHasStarted; + _scheduler = scheduler; _concurrency = options.Value.Concurrency; // We need to at least create Window here in case any handler does loggin in their constructor @@ -262,6 +266,7 @@ await LanguageProtocolEventingHelper.Run( _initializeHandlers.Union(_collection.Select(z => z.Handler).OfType()), (handler, ct) => handler.OnInitialize(this, @params, ct), _concurrency, + _scheduler, token ).ConfigureAwait(false); @@ -281,6 +286,7 @@ await LanguageProtocolEventingHelper.Run( _initializedHandlers.Union(_collection.Select(z => z.Handler).OfType()), (handler, ct) => handler.OnInitialized(this, @params, serverParams, ct), _concurrency, + _scheduler, token ).ConfigureAwait(false); @@ -299,6 +305,7 @@ await LanguageProtocolEventingHelper.Run( _startedHandlers.Union(_collection.Select(z => z.Handler).OfType()), (handler, ct) => handler.OnStarted(this, ct), _concurrency, + _scheduler, token ).ConfigureAwait(false); @@ -395,7 +402,7 @@ private Supports UseOrTryAndFindCapability(Supports supports) where T : bool IResponseRouter.TryGetRequest(long id, [NotNullWhen(true)] out string method, [NotNullWhen(true)] out TaskCompletionSource pendingTask) => _responseRouter.TryGetRequest(id, out method, out pendingTask); - public Task WasStarted => _initializeComplete.ToTask(); + public Task WasStarted => _initializeComplete.ToTask(_scheduler); public void Dispose() { diff --git a/src/Client/LanguageClientOptionsExtensions.cs b/src/Client/LanguageClientOptionsExtensions.cs index 2c10ae955..32fe472e1 100644 --- a/src/Client/LanguageClientOptionsExtensions.cs +++ b/src/Client/LanguageClientOptionsExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Reactive.Concurrency; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -87,6 +88,54 @@ public static LanguageClientOptions WithClientCapabilities(this LanguageClientOp return options; } + /// + /// Sets both input and output schedulers to the same scheduler + /// + /// + /// + /// + public static LanguageClientOptions WithScheduler(this LanguageClientOptions options, IScheduler inputScheduler) + { + options.InputScheduler = options.OutputScheduler = options.DefaultScheduler = inputScheduler; + return options; + } + + /// + /// Sets the scheduler used during reading input + /// + /// + /// + /// + public static LanguageClientOptions WithInputScheduler(this LanguageClientOptions options, IScheduler inputScheduler) + { + options.InputScheduler = inputScheduler; + return options; + } + + /// + /// Sets the default scheduler to be used when scheduling other tasks + /// + /// + /// + /// + public static LanguageClientOptions WithDefaultScheduler(this LanguageClientOptions options, IScheduler defaultScheduler) + { + options.DefaultScheduler = defaultScheduler; + return options; + } + + /// + /// Sets the scheduler use during writing output + /// + /// + /// + /// + public static LanguageClientOptions WithOutputScheduler(this LanguageClientOptions options, IScheduler outputScheduler) + { + options.OutputScheduler = outputScheduler; + return options; + } + public static LanguageClientOptions OnInitialize(this LanguageClientOptions options, OnLanguageClientInitializeDelegate @delegate) { options.Services.AddSingleton(@delegate); diff --git a/src/Client/LanguageClientRegistrationManager.cs b/src/Client/LanguageClientRegistrationManager.cs index 828a7b596..b6703e4c3 100644 --- a/src/Client/LanguageClientRegistrationManager.cs +++ b/src/Client/LanguageClientRegistrationManager.cs @@ -2,6 +2,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Reactive.Concurrency; using System.Reactive.Linq; using System.Reactive.Subjects; using System.Threading; @@ -26,7 +27,7 @@ internal class LanguageClientRegistrationManager : IRegisterCapabilityHandler, I private readonly ILspHandlerTypeDescriptorProvider _handlerTypeDescriptorProvider; private readonly ILogger _logger; private readonly ConcurrentDictionary _registrations; - private readonly ReplaySubject> _registrationSubject = new ReplaySubject>(1); + private readonly ReplaySubject> _registrationSubject = new ReplaySubject>(1, Scheduler.Immediate); public LanguageClientRegistrationManager( ISerializer serializer, diff --git a/src/Client/LanguageClientWorkspaceFoldersManager.cs b/src/Client/LanguageClientWorkspaceFoldersManager.cs index c768335bc..1397dad26 100644 --- a/src/Client/LanguageClientWorkspaceFoldersManager.cs +++ b/src/Client/LanguageClientWorkspaceFoldersManager.cs @@ -2,6 +2,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Reactive.Concurrency; using System.Reactive.Linq; using System.Reactive.Subjects; using System.Threading; @@ -25,7 +26,7 @@ public LanguageClientWorkspaceFoldersManager(IWorkspaceLanguageClient client, IE { _client = client; _workspaceFolders = new ConcurrentDictionary(DocumentUri.Comparer); - _workspaceFoldersSubject = new ReplaySubject>(1); + _workspaceFoldersSubject = new ReplaySubject>(1, Scheduler.Immediate); foreach (var folder in workspaceFolders) { diff --git a/src/Dap.Client/DebugAdapterClient.cs b/src/Dap.Client/DebugAdapterClient.cs index 5d8854b95..f2ec06604 100644 --- a/src/Dap.Client/DebugAdapterClient.cs +++ b/src/Dap.Client/DebugAdapterClient.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reactive.Concurrency; using System.Reactive.Disposables; using System.Reactive.Subjects; using System.Reactive.Threading.Tasks; @@ -30,6 +31,7 @@ public class DebugAdapterClient : JsonRpcServerBase, IDebugAdapterClient, IDebug private readonly IEnumerable _startedDelegates; private readonly IEnumerable _startedHandlers; private readonly InstanceHasStarted _instanceHasStarted; + private readonly IScheduler _scheduler; private readonly CompositeDisposable _disposable = new CompositeDisposable(); private readonly Connection _connection; private readonly DapReceiver _receiver; @@ -97,7 +99,8 @@ internal DebugAdapterClient( IEnumerable initializedDelegates, IEnumerable initializedHandlers, IEnumerable startedHandlers, - InstanceHasStarted instanceHasStarted + InstanceHasStarted instanceHasStarted, + IScheduler scheduler ) : base(collection, responseRouter) { _settingsBag = settingsBag; @@ -114,6 +117,7 @@ InstanceHasStarted instanceHasStarted _initializedHandlers = initializedHandlers; _startedHandlers = startedHandlers; _instanceHasStarted = instanceHasStarted; + _scheduler = scheduler; _concurrency = options.Value.Concurrency; _disposable.Add(collection.Add(this)); @@ -127,6 +131,7 @@ await DebugAdapterEventingHelper.Run( _initializeHandlers.Union(_collection.Select(z => z.Handler).OfType()), (handler, ct) => handler.OnInitialize(this, ClientSettings, ct), _concurrency, + _scheduler, token ).ConfigureAwait(false); @@ -145,10 +150,11 @@ await DebugAdapterEventingHelper.Run( _initializedHandlers.Union(_collection.Select(z => z.Handler).OfType()), (handler, ct) => handler.OnInitialized(this, ClientSettings, ServerSettings, ct), _concurrency, + _scheduler, token ).ConfigureAwait(false); - await _initializedComplete.ToTask(token); + await _initializedComplete.ToTask(token, _scheduler); await DebugAdapterEventingHelper.Run( _startedDelegates, @@ -156,6 +162,7 @@ await DebugAdapterEventingHelper.Run( _startedHandlers.Union(_collection.Select(z => z.Handler).OfType()), (handler, ct) => handler.OnStarted(this, ct), _concurrency, + _scheduler, token ).ConfigureAwait(false); diff --git a/src/Dap.Client/DebugAdapterClientOptionsExtensions.cs b/src/Dap.Client/DebugAdapterClientOptionsExtensions.cs index 4b3f8236c..6c10f6646 100644 --- a/src/Dap.Client/DebugAdapterClientOptionsExtensions.cs +++ b/src/Dap.Client/DebugAdapterClientOptionsExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Reactive.Concurrency; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -21,6 +22,54 @@ public static DebugAdapterClientOptions WithRequestProcessIdentifier(this DebugA return options; } + /// + /// Sets both input and output schedulers to the same scheduler + /// + /// + /// + /// + public static DebugAdapterClientOptions WithScheduler(this DebugAdapterClientOptions options, IScheduler inputScheduler) + { + options.InputScheduler = options.OutputScheduler = options.DefaultScheduler = inputScheduler; + return options; + } + + /// + /// Sets the scheduler used during reading input + /// + /// + /// + /// + public static DebugAdapterClientOptions WithInputScheduler(this DebugAdapterClientOptions options, IScheduler inputScheduler) + { + options.InputScheduler = inputScheduler; + return options; + } + + /// + /// Sets the default scheduler to be used when scheduling other tasks + /// + /// + /// + /// + public static DebugAdapterClientOptions WithDefaultScheduler(this DebugAdapterClientOptions options, IScheduler defaultScheduler) + { + options.DefaultScheduler = defaultScheduler; + return options; + } + + /// + /// Sets the scheduler use during writing output + /// + /// + /// + /// + public static DebugAdapterClientOptions WithOutputScheduler(this DebugAdapterClientOptions options, IScheduler outputScheduler) + { + options.OutputScheduler = outputScheduler; + return options; + } + public static DebugAdapterClientOptions OnInitialize(this DebugAdapterClientOptions options, OnDebugAdapterClientInitializeDelegate @delegate) { options.Services.AddSingleton(@delegate); diff --git a/src/Dap.Client/ProgressObservable.cs b/src/Dap.Client/ProgressObservable.cs index c961689b3..8860ba5c9 100644 --- a/src/Dap.Client/ProgressObservable.cs +++ b/src/Dap.Client/ProgressObservable.cs @@ -1,4 +1,5 @@ using System; +using System.Reactive.Concurrency; using System.Reactive.Disposables; using System.Reactive.Subjects; using OmniSharp.Extensions.DebugAdapter.Protocol; @@ -14,7 +15,7 @@ internal class ProgressObservable : IProgressObservable, IObserver(1); + _dataSubject = new ReplaySubject(1, Scheduler.Immediate); _disposable = new CompositeDisposable { Disposable.Create(_dataSubject.OnCompleted) }; ProgressToken = token; diff --git a/src/Dap.Protocol/Feature/Events/ProgressFeature.cs b/src/Dap.Protocol/Feature/Events/ProgressFeature.cs index f0df93eac..16ebd60a5 100644 --- a/src/Dap.Protocol/Feature/Events/ProgressFeature.cs +++ b/src/Dap.Protocol/Feature/Events/ProgressFeature.cs @@ -23,7 +23,7 @@ public abstract record ProgressEvent public string? Message { get; init; } } - [Parallel] + [Serial] [Method(EventNames.ProgressStart, Direction.ServerToClient)] [ GenerateHandler, @@ -60,7 +60,7 @@ public record ProgressStartEvent : ProgressEvent, IRequest public int? Percentage { get; init; } } - [Parallel] + [Serial] [Method(EventNames.ProgressUpdate, Direction.ServerToClient)] [ GenerateHandler, @@ -76,7 +76,7 @@ public record ProgressUpdateEvent : ProgressEvent, IRequest public double? Percentage { get; init; } } - [Parallel] + [Serial] [Method(EventNames.ProgressEnd, Direction.ServerToClient)] [ GenerateHandler, diff --git a/src/Dap.Server/DebugAdapterServer.cs b/src/Dap.Server/DebugAdapterServer.cs index 880b6e7db..7e6cde49a 100644 --- a/src/Dap.Server/DebugAdapterServer.cs +++ b/src/Dap.Server/DebugAdapterServer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reactive.Concurrency; using System.Reactive.Disposables; using System.Reactive.Subjects; using System.Reactive.Threading.Tasks; @@ -30,6 +31,7 @@ public class DebugAdapterServer : JsonRpcServerBase, IDebugAdapterServer, IDebug private readonly IEnumerable _startedDelegates; private readonly IEnumerable _startedHandlers; private readonly InstanceHasStarted _instanceHasStarted; + private readonly IScheduler _scheduler; private readonly IServiceProvider _serviceProvider; private readonly CompositeDisposable _disposable = new CompositeDisposable(); private readonly Connection _connection; @@ -100,7 +102,8 @@ internal DebugAdapterServer( IEnumerable initializeHandlers, IEnumerable initializedHandlers, IEnumerable startedHandlers, - InstanceHasStarted instanceHasStarted + InstanceHasStarted instanceHasStarted, + IScheduler scheduler ) : base(collection, responseRouter) { _capabilities = capabilities; @@ -117,6 +120,7 @@ InstanceHasStarted instanceHasStarted _initializedHandlers = initializedHandlers; _startedHandlers = startedHandlers; _instanceHasStarted = instanceHasStarted; + _scheduler = scheduler; _concurrency = options.Value.Concurrency; _disposable.Add(collection.Add(this)); @@ -142,7 +146,7 @@ public async Task Initialize(CancellationToken token) _connection.Open(); try { - _initializingTask = _initializeComplete.ToTask(token); + _initializingTask = _initializeComplete.ToTask(token, _scheduler); await _initializingTask.ConfigureAwait(false); await DebugAdapterEventingHelper.Run( _startedDelegates, @@ -150,6 +154,7 @@ await DebugAdapterEventingHelper.Run( _startedHandlers.Union(_collection.Select(z => z.Handler).OfType()), (handler, ct) => handler.OnStarted(this, ct), _concurrency, + _scheduler, token ).ConfigureAwait(false); _instanceHasStarted.Started = true; @@ -181,6 +186,7 @@ await DebugAdapterEventingHelper.Run( _initializeHandlers.Union(_collection.Select(z => z.Handler).OfType()), (handler, ct) => handler.OnInitialize(this, request, ct), _concurrency, + _scheduler, cancellationToken ).ConfigureAwait(false); @@ -231,6 +237,7 @@ await DebugAdapterEventingHelper.Run( _initializedHandlers.Union(_collection.Select(z => z.Handler).OfType()), (handler, ct) => handler.OnInitialized(this, request, response, ct), _concurrency, + _scheduler, cancellationToken ).ConfigureAwait(false); diff --git a/src/Dap.Server/DebugAdapterServerOptionsExtensions.cs b/src/Dap.Server/DebugAdapterServerOptionsExtensions.cs index 89aa784bb..f25ed04e0 100644 --- a/src/Dap.Server/DebugAdapterServerOptionsExtensions.cs +++ b/src/Dap.Server/DebugAdapterServerOptionsExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Reactive.Concurrency; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -21,6 +22,54 @@ public static DebugAdapterServerOptions WithRequestProcessIdentifier(this DebugA return options; } + /// + /// Sets both input and output schedulers to the same scheduler + /// + /// + /// + /// + public static DebugAdapterServerOptions WithScheduler(this DebugAdapterServerOptions options, IScheduler inputScheduler) + { + options.InputScheduler = options.OutputScheduler = options.DefaultScheduler = inputScheduler; + return options; + } + + /// + /// Sets the scheduler used during reading input + /// + /// + /// + /// + public static DebugAdapterServerOptions WithInputScheduler(this DebugAdapterServerOptions options, IScheduler inputScheduler) + { + options.InputScheduler = inputScheduler; + return options; + } + + /// + /// Sets the default scheduler to be used when scheduling other tasks + /// + /// + /// + /// + public static DebugAdapterServerOptions WithDefaultScheduler(this DebugAdapterServerOptions options, IScheduler defaultScheduler) + { + options.DefaultScheduler = defaultScheduler; + return options; + } + + /// + /// Sets the scheduler use during writing output + /// + /// + /// + /// + public static DebugAdapterServerOptions WithOutputScheduler(this DebugAdapterServerOptions options, IScheduler outputScheduler) + { + options.OutputScheduler = outputScheduler; + return options; + } + public static DebugAdapterServerOptions OnInitialize(this DebugAdapterServerOptions options, OnDebugAdapterServerInitializeDelegate @delegate) { options.Services.AddSingleton(@delegate); diff --git a/src/Dap.Shared/DebugAdapterEventingHelper.cs b/src/Dap.Shared/DebugAdapterEventingHelper.cs index 60176311f..4db62e462 100644 --- a/src/Dap.Shared/DebugAdapterEventingHelper.cs +++ b/src/Dap.Shared/DebugAdapterEventingHelper.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reactive.Concurrency; using System.Reactive.Linq; using System.Reactive.Threading.Tasks; using System.Threading; @@ -16,6 +17,7 @@ public static Task Run( IEnumerable handlers, Func executeHandler, int? concurrency, + IScheduler scheduler, CancellationToken cancellationToken ) { @@ -28,13 +30,13 @@ CancellationToken cancellationToken { return events.Merge(concurrency.Value) .LastOrDefaultAsync() - .ToTask(cancellationToken); + .ToTask(cancellationToken, scheduler); } return events .Merge() .LastOrDefaultAsync() - .ToTask(cancellationToken); + .ToTask(cancellationToken, scheduler); } } } diff --git a/src/Dap.Testing/DebugAdapterProtocolTestBase.cs b/src/Dap.Testing/DebugAdapterProtocolTestBase.cs index 92eabb3bf..2cbe87d4e 100644 --- a/src/Dap.Testing/DebugAdapterProtocolTestBase.cs +++ b/src/Dap.Testing/DebugAdapterProtocolTestBase.cs @@ -45,10 +45,11 @@ Action serverOptionsAction options .WithLoggerFactory(TestOptions.ClientLoggerFactory) .ConfigureLogging( - x => { - x.SetMinimumLevel(LogLevel.Trace); - } + x => { x.SetMinimumLevel(LogLevel.Trace); } ) + .WithInputScheduler(options.InputScheduler) + .WithOutputScheduler(options.OutputScheduler) + .WithDefaultScheduler(options.DefaultScheduler) .Services .AddTransient(typeof(IPipelineBehavior<,>), typeof(SettlePipeline<,>)) .AddSingleton(ClientEvents as IRequestSettler); @@ -61,11 +62,10 @@ Action serverOptionsAction options => { options .WithLoggerFactory(TestOptions.ServerLoggerFactory) - .ConfigureLogging( - x => { - x.SetMinimumLevel(LogLevel.Trace); - } - ) + .ConfigureLogging(x => { x.SetMinimumLevel(LogLevel.Trace); }) + .WithInputScheduler(options.InputScheduler) + .WithOutputScheduler(options.OutputScheduler) + .WithDefaultScheduler(options.DefaultScheduler) .Services .AddTransient(typeof(IPipelineBehavior<,>), typeof(SettlePipeline<,>)) .AddSingleton(ServerEvents as IRequestSettler); @@ -77,10 +77,9 @@ Action serverOptionsAction Disposable.Add(_client); Disposable.Add(_server); - return await Observable.FromAsync(_client.Initialize).ForkJoin( - Observable.FromAsync(_server.Initialize), - (_, _) => ( _client, _server ) - ).ToTask(CancellationToken).ConfigureAwait(false); + return await Observable.FromAsync(_client.Initialize) + .ForkJoin(Observable.FromAsync(_server.Initialize), (_, _) => ( _client, _server )) + .ToTask(CancellationToken); } } } diff --git a/src/Dap.Testing/DebugAdapterServerTestBase.cs b/src/Dap.Testing/DebugAdapterServerTestBase.cs index 7654f2cf1..1c53d4354 100644 --- a/src/Dap.Testing/DebugAdapterServerTestBase.cs +++ b/src/Dap.Testing/DebugAdapterServerTestBase.cs @@ -37,6 +37,9 @@ protected virtual async Task InitializeClient(Action), typeof(SettlePipeline<,>)) .AddSingleton(Events as IRequestSettler); diff --git a/src/JsonRpc.Generators/Helpers.cs b/src/JsonRpc.Generators/Helpers.cs index ebed8a799..f7559140d 100644 --- a/src/JsonRpc.Generators/Helpers.cs +++ b/src/JsonRpc.Generators/Helpers.cs @@ -802,8 +802,40 @@ public static ArrowExpressionClauseSyntax GetRequestInvokeExpression() => ) ); - public static ArrowExpressionClauseSyntax GetPartialInvokeExpression(TypeSyntax responseType) => - ArrowExpressionClause( + public static ArrowExpressionClauseSyntax GetPartialInvokeExpression(TypeSyntax responseType, TypeSyntax? partialItemType) + { + var realResponseType = responseType is NullableTypeSyntax nts ? nts.ElementType : responseType; + var factoryArgument = Argument( + SimpleLambdaExpression( + Parameter(Identifier("value")), + ObjectCreationExpression(realResponseType) + .WithArgumentList(ArgumentList(SingletonSeparatedList(Argument(IdentifierName("value"))))) + ) + ); + var arguments = new[] { + Argument( + IdentifierName(@"request") + ), + factoryArgument, + Argument(IdentifierName("cancellationToken")) + }; + if (partialItemType is {}) + { + var realPartialItemType = partialItemType is NullableTypeSyntax nts2 ? nts2.ElementType : partialItemType; + arguments = new[] { + arguments[0], + arguments[1], + Argument( + SimpleLambdaExpression( + Parameter(Identifier("value")), + ObjectCreationExpression(realPartialItemType) + .WithArgumentList(ArgumentList(SingletonSeparatedList(Argument(IdentifierName("value"))))) + ) + ), + arguments[2] + }; + } + return ArrowExpressionClause( InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, @@ -815,26 +847,9 @@ public static ArrowExpressionClauseSyntax GetPartialInvokeExpression(TypeSyntax IdentifierName("MonitorUntil") ) ) - .WithArgumentList( - ArgumentList( - SeparatedList( - new[] { - Argument( - IdentifierName(@"request") - ), - Argument( - SimpleLambdaExpression( - Parameter(Identifier("value")), - ObjectCreationExpression(responseType is NullableTypeSyntax nts ? nts.ElementType : responseType) - .WithArgumentList(ArgumentList(SingletonSeparatedList(Argument(IdentifierName("value"))))) - ) - ), - Argument(IdentifierName("cancellationToken")) - } - ) - ) - ) + .WithArgumentList(ArgumentList(SeparatedList(arguments))) ); + } public static string GetExtensionClassName(INamedTypeSymbol symbol) => SpecialCasedHandlerFullName(symbol).Split('.').Last() + "Extensions"; diff --git a/src/JsonRpc.Generators/Strategies/SendMethodRequestStrategy.cs b/src/JsonRpc.Generators/Strategies/SendMethodRequestStrategy.cs index 47eb046d8..fdcf99139 100644 --- a/src/JsonRpc.Generators/Strategies/SendMethodRequestStrategy.cs +++ b/src/JsonRpc.Generators/Strategies/SendMethodRequestStrategy.cs @@ -59,7 +59,7 @@ public IEnumerable Apply(ExtensionMethodContext extensi ) ) .WithParameterList(parameterList) - .WithExpressionBody(Helpers.GetPartialInvokeExpression(request.Response.Syntax)) + .WithExpressionBody(Helpers.GetPartialInvokeExpression(request.Response.Syntax, request.PartialItem.Syntax)) .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)); yield break; } @@ -91,7 +91,7 @@ public IEnumerable Apply(ExtensionMethodContext extensi ) ) .WithParameterList(parameterList) - .WithExpressionBody(Helpers.GetPartialInvokeExpression(request.Response.Syntax)) + .WithExpressionBody(Helpers.GetPartialInvokeExpression(request.Response.Syntax, default)) .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)); yield break; } diff --git a/src/JsonRpc.Testing/JsonRpcTestBase.cs b/src/JsonRpc.Testing/JsonRpcTestBase.cs index 4b0accc07..991ca6165 100644 --- a/src/JsonRpc.Testing/JsonRpcTestBase.cs +++ b/src/JsonRpc.Testing/JsonRpcTestBase.cs @@ -1,7 +1,9 @@ using System; using System.Diagnostics; using System.Reactive; +using System.Reactive.Concurrency; using System.Reactive.Disposables; +using System.Reactive.Threading.Tasks; using System.Threading; using System.Threading.Tasks; diff --git a/src/JsonRpc.Testing/JsonRpcTestOptions.cs b/src/JsonRpc.Testing/JsonRpcTestOptions.cs index 13df29487..1b998acab 100644 --- a/src/JsonRpc.Testing/JsonRpcTestOptions.cs +++ b/src/JsonRpc.Testing/JsonRpcTestOptions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO.Pipelines; using System.Linq; +using System.Reactive.Concurrency; using System.Reflection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; @@ -24,6 +25,8 @@ public JsonRpcTestOptions(ILoggerFactory clientLoggerFactory, ILoggerFactory ser public ILoggerFactory ClientLoggerFactory { get; internal set; } = NullLoggerFactory.Instance; public ILoggerFactory ServerLoggerFactory { get; internal set; } = NullLoggerFactory.Instance; + public IScheduler ClientScheduler { get; internal set; } = TaskPoolScheduler.Default; + public IScheduler ServerScheduler { get; internal set; } = TaskPoolScheduler.Default; public TimeSpan WaitTime { get; internal set; } = TimeSpan.FromMilliseconds(50); public TimeSpan Timeout { get; internal set; } = TimeSpan.FromMilliseconds(500); public TimeSpan CancellationTimeout { get; internal set; } = TimeSpan.FromSeconds(50); diff --git a/src/JsonRpc.Testing/JsonRpcTestOptionsExtensions.cs b/src/JsonRpc.Testing/JsonRpcTestOptionsExtensions.cs index 1fa284b70..b367ff631 100644 --- a/src/JsonRpc.Testing/JsonRpcTestOptionsExtensions.cs +++ b/src/JsonRpc.Testing/JsonRpcTestOptionsExtensions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO.Pipelines; using System.Linq; +using System.Reactive.Concurrency; using System.Reflection; using Microsoft.Extensions.Logging; @@ -21,6 +22,30 @@ public static JsonRpcTestOptions WithClientLoggerFactory(this JsonRpcTestOptions return options; } + public static JsonRpcTestOptions WithLoggerFactory(this JsonRpcTestOptions options, ILoggerFactory loggerFactory) + { + options.ClientLoggerFactory = options.ServerLoggerFactory = loggerFactory; + return options; + } + + public static JsonRpcTestOptions WithServerScheduler(this JsonRpcTestOptions options, IScheduler scheduler) + { + options.ServerScheduler = scheduler; + return options; + } + + public static JsonRpcTestOptions WithClientScheduler(this JsonRpcTestOptions options, IScheduler scheduler) + { + options.ClientScheduler = scheduler; + return options; + } + + public static JsonRpcTestOptions WithScheduler(this JsonRpcTestOptions options, IScheduler scheduler) + { + options.ClientScheduler = options.ServerScheduler = scheduler; + return options; + } + public static JsonRpcTestOptions WithWaitTime(this JsonRpcTestOptions options, TimeSpan waitTime) { options.WaitTime = waitTime; diff --git a/src/JsonRpc.Testing/Settler.cs b/src/JsonRpc.Testing/Settler.cs index 2fc758d70..f25ee60e3 100644 --- a/src/JsonRpc.Testing/Settler.cs +++ b/src/JsonRpc.Testing/Settler.cs @@ -60,7 +60,7 @@ public Settler(JsonRpcTestOptions options, CancellationToken cancellationToken, _requester = subject.AsObserver(); } - public Task SettleNext() => SettleNextInternal().ToTask(_cancellationToken); + public Task SettleNext() => SettleNextInternal().ToTask(_cancellationToken, _scheduler); public IObservable SettleNextInternal() => _settle .Catch(_ => _timeoutValue) diff --git a/src/JsonRpc/Client/OutgoingNotification.cs b/src/JsonRpc/Client/OutgoingNotification.cs index 750249a02..a1480ae91 100644 --- a/src/JsonRpc/Client/OutgoingNotification.cs +++ b/src/JsonRpc/Client/OutgoingNotification.cs @@ -1,6 +1,6 @@ namespace OmniSharp.Extensions.JsonRpc.Client { - public class OutgoingNotification + public record OutgoingNotification { public string Method { get; set; } = null!; diff --git a/src/JsonRpc/Client/OutgoingRequest.cs b/src/JsonRpc/Client/OutgoingRequest.cs index 6252de1aa..787e00591 100644 --- a/src/JsonRpc/Client/OutgoingRequest.cs +++ b/src/JsonRpc/Client/OutgoingRequest.cs @@ -2,7 +2,7 @@ namespace OmniSharp.Extensions.JsonRpc.Client { - public class OutgoingRequest + public record OutgoingRequest { public object? Id { get; set; } diff --git a/src/JsonRpc/Client/OutgoingResponse.cs b/src/JsonRpc/Client/OutgoingResponse.cs index 0b4dfa3fe..582e53c58 100644 --- a/src/JsonRpc/Client/OutgoingResponse.cs +++ b/src/JsonRpc/Client/OutgoingResponse.cs @@ -2,7 +2,7 @@ namespace OmniSharp.Extensions.JsonRpc.Client { - public class OutgoingResponse + public record OutgoingResponse { public OutgoingResponse(object id, ServerRequest request) { diff --git a/src/JsonRpc/JsonRpc.csproj b/src/JsonRpc/JsonRpc.csproj index 81d2c2328..084fab058 100644 --- a/src/JsonRpc/JsonRpc.csproj +++ b/src/JsonRpc/JsonRpc.csproj @@ -7,16 +7,17 @@ Primitives for working with JsonRpc. This library is used as the base for communication with language servers - - - - - - - - - - + + + + + + + + + + + diff --git a/src/JsonRpc/JsonRpcServerOptionsBase.cs b/src/JsonRpc/JsonRpcServerOptionsBase.cs index 6a513ba3e..ad3d787fe 100644 --- a/src/JsonRpc/JsonRpcServerOptionsBase.cs +++ b/src/JsonRpc/JsonRpcServerOptionsBase.cs @@ -37,7 +37,8 @@ public ILoggerFactory LoggerFactory public IRequestProcessIdentifier? RequestProcessIdentifier { get; set; } public int? Concurrency { get; set; } public IScheduler InputScheduler { get; set; } = TaskPoolScheduler.Default; - public IScheduler OutputScheduler { get; set; } = TaskPoolScheduler.Default; + public IScheduler OutputScheduler { get; set; } = Scheduler.Immediate; + public IScheduler DefaultScheduler { get; set; } = TaskPoolScheduler.Default; public CreateResponseExceptionHandler? CreateResponseException { get; set; } public OnUnhandledExceptionHandler? OnUnhandledException { get; set; } public bool SupportsContentModified { get; set; } = true; diff --git a/src/JsonRpc/JsonRpcServerOptionsExtensions.cs b/src/JsonRpc/JsonRpcServerOptionsExtensions.cs index b2a821b75..6c341cc7a 100644 --- a/src/JsonRpc/JsonRpcServerOptionsExtensions.cs +++ b/src/JsonRpc/JsonRpcServerOptionsExtensions.cs @@ -24,7 +24,7 @@ public static JsonRpcServerOptions WithAssemblyAttributeScanning(this JsonRpcSer /// public static JsonRpcServerOptions WithScheduler(this JsonRpcServerOptions options, IScheduler inputScheduler) { - options.InputScheduler = options.OutputScheduler = inputScheduler; + options.InputScheduler = options.OutputScheduler = options.DefaultScheduler = inputScheduler; return options; } @@ -40,6 +40,18 @@ public static JsonRpcServerOptions WithInputScheduler(this JsonRpcServerOptions return options; } + /// + /// Sets the default scheduler to be used when scheduling other tasks + /// + /// + /// + /// + public static JsonRpcServerOptions WithDefaultScheduler(this JsonRpcServerOptions options, IScheduler defaultScheduler) + { + options.DefaultScheduler = defaultScheduler; + return options; + } + /// /// Sets the scheduler use during writing output /// diff --git a/src/JsonRpc/JsonRpcServerServiceCollectionExtensions.cs b/src/JsonRpc/JsonRpcServerServiceCollectionExtensions.cs index 35538dfab..1859fd078 100644 --- a/src/JsonRpc/JsonRpcServerServiceCollectionExtensions.cs +++ b/src/JsonRpc/JsonRpcServerServiceCollectionExtensions.cs @@ -58,6 +58,8 @@ internal static IContainer AddJsonRpcServerCore(this IContainer container, Js reuse: Reuse.Singleton ); + container.RegisterInstance(options.DefaultScheduler); + container.RegisterMany( serviceTypeCondition: type => type.IsInterface, reuse: Reuse.Singleton diff --git a/src/JsonRpc/OutputHandler.cs b/src/JsonRpc/OutputHandler.cs index 2b167bdc9..ef852869e 100644 --- a/src/JsonRpc/OutputHandler.cs +++ b/src/JsonRpc/OutputHandler.cs @@ -3,12 +3,14 @@ using System.ComponentModel; using System.IO.Pipelines; using System.Linq; +using System.Reactive; using System.Reactive.Concurrency; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reactive.Subjects; using System.Text; using System.Threading; +using System.Threading.Channels; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -20,11 +22,16 @@ public class OutputHandler : IOutputHandler private readonly ISerializer _serializer; private readonly IEnumerable _outputFilters; private readonly ILogger _logger; - private readonly Subject _queue; - private readonly ReplaySubject _delayedQueue; + + + private readonly ChannelReader _queue; + private readonly Queue _delayedQueue; private readonly TaskCompletionSource _outputIsFinished; private readonly CompositeDisposable _disposable; private bool _delayComplete; + private readonly CancellationTokenSource _stopProcessing; + private readonly Channel _channel; + private readonly ChannelWriter _writer; public OutputHandler( PipeWriter pipeWriter, @@ -38,22 +45,26 @@ ILogger logger _serializer = serializer; _outputFilters = outputFilters.ToArray(); _logger = logger; - _queue = new Subject(); - _delayedQueue = new ReplaySubject(); + _delayedQueue = new Queue(); _outputIsFinished = new TaskCompletionSource(); + _channel = Channel.CreateUnbounded( + new UnboundedChannelOptions() { + AllowSynchronousContinuations = true, + SingleReader = true, + SingleWriter = false + } + ); + _queue = _channel.Reader; + _writer = _channel.Writer; + + _stopProcessing = new CancellationTokenSource(); _disposable = new CompositeDisposable { - _queue - .ObserveOn(scheduler) - .Select(value => Observable.FromAsync(ct => ProcessOutputStream(value, ct))) - .Concat() - .Subscribe(), - _delayedQueue - .ToArray() - .SelectMany(z => z) - .Subscribe(_queue.OnNext), - _queue, - _delayedQueue + Disposable.Create(() => _stopProcessing.Cancel()), + _stopProcessing, + Observable.FromAsync(() => ProcessOutputStream(_stopProcessing.Token)) + .Do(_ => { }, e => _logger.LogCritical(e, "unhandled exception")) + .Subscribe() }; } @@ -66,30 +77,37 @@ public void Send(object? value) { try { - if (_queue.IsDisposed || _disposable.IsDisposed || value == null) return; - if (!ShouldSend(value)) + if (_disposable.IsDisposed || value == null) return; + if (!ShouldSend(value) && !_delayComplete) { - if (_delayComplete || _delayedQueue.IsDisposed || !_delayedQueue.HasObservers) return; - _delayedQueue.OnNext(value); + _delayedQueue.Enqueue(value); } else { - _queue.OnNext(value); + _writer.TryWrite(value); } } - catch (ObjectDisposedException) { } + catch (ObjectDisposedException) + { + } } public void Initialized() { - if (_delayComplete || _delayedQueue.IsDisposed || !_delayedQueue.HasObservers) return; - _delayedQueue.OnCompleted(); + if (_delayComplete) return; + while (_delayedQueue.Count > 0) + { + var item = _delayedQueue.Dequeue(); + _writer.TryWrite(item); + } + _delayComplete = true; - _delayedQueue.Dispose(); + _delayedQueue.Clear(); } public async Task StopAsync() { + _channel.Writer.TryComplete(); await _pipeWriter.CompleteAsync().ConfigureAwait(false); _disposable.Dispose(); } @@ -105,17 +123,21 @@ internal async Task WriteAndFlush() await _pipeWriter.CompleteAsync().ConfigureAwait(false); } - private async Task ProcessOutputStream(object value, CancellationToken cancellationToken) + private async Task ProcessOutputStream(CancellationToken cancellationToken) { try { + do + { + var value = await _queue.ReadAsync(cancellationToken); // _logger.LogTrace("Writing out {@Value}", value); - // TODO: this will be part of the serialization refactor to make streaming first class - var content = _serializer.SerializeObject(value); - var contentBytes = Encoding.UTF8.GetBytes(content).AsMemory(); - await _pipeWriter.WriteAsync(Encoding.UTF8.GetBytes($"Content-Length: {contentBytes.Length}\r\n\r\n"), cancellationToken).ConfigureAwait(false); - await _pipeWriter.WriteAsync(contentBytes, cancellationToken).ConfigureAwait(false); - await _pipeWriter.FlushAsync(cancellationToken).ConfigureAwait(false); + // TODO: this will be part of the serialization refactor to make streaming first class + var content = _serializer.SerializeObject(value); + var contentBytes = Encoding.UTF8.GetBytes(content).AsMemory(); + await _pipeWriter.WriteAsync(Encoding.UTF8.GetBytes($"Content-Length: {contentBytes.Length}\r\n\r\n"), cancellationToken).ConfigureAwait(false); + await _pipeWriter.WriteAsync(contentBytes, cancellationToken).ConfigureAwait(false); + await _pipeWriter.FlushAsync(cancellationToken).ConfigureAwait(false); + } while (true); } catch (OperationCanceledException ex) when (ex.CancellationToken != cancellationToken) { @@ -134,12 +156,14 @@ private async Task ProcessOutputStream(object value, CancellationToken cancellat private void Error(Exception ex) { _outputIsFinished.TrySetResult(ex); + _writer.TryComplete(); _disposable.Dispose(); } public void Dispose() { _outputIsFinished.TrySetResult(null); + _writer.TryComplete(); _disposable.Dispose(); } } diff --git a/src/JsonRpc/ProcessScheduler.cs b/src/JsonRpc/ProcessScheduler.cs index 4540ea95f..ae0da9171 100644 --- a/src/JsonRpc/ProcessScheduler.cs +++ b/src/JsonRpc/ProcessScheduler.cs @@ -34,7 +34,7 @@ IScheduler scheduler var observableQueue = new BehaviorSubject<(RequestProcessType type, ReplaySubject> observer, Subject? contentModifiedSource)>( - ( RequestProcessType.Serial, new ReplaySubject>(int.MaxValue), supportContentModified ? new Subject() : null ) + ( RequestProcessType.Serial, new ReplaySubject>(int.MaxValue, Scheduler.Immediate), supportContentModified ? new Subject() : null ) ); cd.Add( @@ -52,7 +52,7 @@ IScheduler scheduler logger.LogDebug("Completing existing request process type {Type}", observableQueue.Value.type); observableQueue.Value.observer.OnCompleted(); - observableQueue.OnNext(( item.type, new ReplaySubject>(int.MaxValue), supportContentModified ? new Subject() : null )); + observableQueue.OnNext(( item.type, new ReplaySubject>(int.MaxValue, Scheduler.Immediate), supportContentModified ? new Subject() : null )); } logger.LogDebug("Queueing {Type}:{Name} request for processing", item.type, item.name); diff --git a/src/Protocol/AbstractHandlers.cs b/src/Protocol/AbstractHandlers.cs index 263864bc5..4fa7cc9c3 100644 --- a/src/Protocol/AbstractHandlers.cs +++ b/src/Protocol/AbstractHandlers.cs @@ -141,10 +141,14 @@ CancellationToken cancellationToken } var subject = new AsyncSubject(); + var task = subject + .Select(_factory) + .ToTask(cancellationToken, _progressManager.Scheduler) + .ConfigureAwait(false); // in the event nothing is emitted... subject.OnNext(default!); Handle(request, subject, cancellationToken); - return _factory(await subject); + return await task; } protected abstract void Handle(TParams request, IObserver results, CancellationToken cancellationToken); @@ -181,10 +185,14 @@ CancellationToken cancellationToken } var subject = new AsyncSubject(); + var task = subject + .Select(_factory) + .ToTask(cancellationToken, _progressManager.Scheduler) + .ConfigureAwait(false); // in the event nothing is emitted... subject.OnNext(default!); Handle(request, subject, cancellationToken); - return _factory(await subject); + return await task; } protected abstract void Handle(TParams request, IObserver results, CancellationToken cancellationToken); @@ -221,10 +229,14 @@ CancellationToken cancellationToken } var subject = new AsyncSubject(); + var task = subject + .Select(_factory) + .ToTask(cancellationToken, _progressManager.Scheduler) + .ConfigureAwait(false); // in the event nothing is emitted... subject.OnNext(default!); Handle(request, subject, cancellationToken); - return _factory(await subject); + return await task; } protected abstract void Handle(TParams request, IObserver results, CancellationToken cancellationToken); @@ -258,15 +270,18 @@ protected PartialResults(IProgressManager progressManager, Func>(); - var task = subject.Aggregate( - new List(), (acc, items) => { - acc.AddRange(items); - return acc; - } - ) - .ToTask(cancellationToken); + var task = subject + .Aggregate( + new List(), (acc, items) => { + acc.AddRange(items); + return acc; + } + ) + .Select(_factory) + .ToTask(cancellationToken, _progressManager.Scheduler) + .ConfigureAwait(false); Handle(request, subject, cancellationToken); - return _factory(await task.ConfigureAwait(false)); + return await task; } protected abstract void Handle(TParams request, IObserver> results, CancellationToken cancellationToken); @@ -299,19 +314,23 @@ protected PartialResults(IProgressManager progressManager, Func>(); - var task = subject.Aggregate( - new List(), (acc, items) => { - acc.AddRange(items); - return acc; - } - ) - .ToTask(cancellationToken); + var task = subject + .Aggregate( + new List(), (acc, items) => { + acc.AddRange(items); + return acc; + } + ) + .Select(_factory) + .ToTask(cancellationToken, _progressManager.Scheduler) + .ConfigureAwait(false); Handle(request, subject, cancellationToken); - return _factory(await task.ConfigureAwait(false)); + return await task; } protected abstract void Handle(TParams request, IObserver> results, CancellationToken cancellationToken); } + public abstract class PartialResultsCapability : BaseCapability, IJsonRpcRequestHandler @@ -339,15 +358,18 @@ protected PartialResultsCapability(IProgressManager progressManager, Func>(); - var task = subject.Aggregate( - new List(), (acc, items) => { - acc.AddRange(items); - return acc; - } - ) - .ToTask(cancellationToken); + var task = subject + .Aggregate( + new List(), (acc, items) => { + acc.AddRange(items); + return acc; + } + ) + .Select(_factory) + .ToTask(cancellationToken, _progressManager.Scheduler) + .ConfigureAwait(false); Handle(request, subject, cancellationToken); - return _factory(await task.ConfigureAwait(false)); + return await task; } protected abstract void Handle(TParams request, IObserver> results, CancellationToken cancellationToken); diff --git a/src/Protocol/Features/Document/SemanticTokensFeature.cs b/src/Protocol/Features/Document/SemanticTokensFeature.cs index 346aab518..99f1236cc 100644 --- a/src/Protocol/Features/Document/SemanticTokensFeature.cs +++ b/src/Protocol/Features/Document/SemanticTokensFeature.cs @@ -37,7 +37,7 @@ namespace Models ] [RegistrationOptions(typeof(SemanticTokensRegistrationOptions)), Capability(typeof(SemanticTokensCapability))] public partial record SemanticTokensParams : IWorkDoneProgressParams, ITextDocumentIdentifierParams, - IPartialItemRequest + IPartialItemRequest { /// /// The text document. @@ -57,7 +57,7 @@ public partial record SemanticTokensParams : IWorkDoneProgressParams, ITextDocum ] [RegistrationOptions(typeof(SemanticTokensRegistrationOptions)), Capability(typeof(SemanticTokensCapability))] public partial record SemanticTokensDeltaParams : IWorkDoneProgressParams, ITextDocumentIdentifierParams, - IPartialItemRequest, IDoesNotParticipateInRegistration + IPartialItemRequest, IDoesNotParticipateInRegistration { /// /// The text document. @@ -82,7 +82,7 @@ public partial record SemanticTokensDeltaParams : IWorkDoneProgressParams, IText ] [RegistrationOptions(typeof(SemanticTokensRegistrationOptions)), Capability(typeof(SemanticTokensCapability))] public partial record SemanticTokensRangeParams : IWorkDoneProgressParams, ITextDocumentIdentifierParams, - IPartialItemRequest, IDoesNotParticipateInRegistration + IPartialItemRequest, IDoesNotParticipateInRegistration { /// /// The text document. @@ -94,6 +94,7 @@ public partial record SemanticTokensRangeParams : IWorkDoneProgressParams, IText /// public Range Range { get; init; } } + [Parallel] [Method(WorkspaceNames.SemanticTokensRefresh, Direction.ServerToClient)] [GenerateHandler("OmniSharp.Extensions.LanguageServer.Protocol.Workspace"), GenerateHandlerMethods, @@ -102,6 +103,7 @@ public partial record SemanticTokensRangeParams : IWorkDoneProgressParams, IText public partial record SemanticTokensRefreshParams : IRequest { } + public interface ISemanticTokenResult { /// @@ -165,6 +167,10 @@ public partial record SemanticTokensPartialResult /// https://github.com/microsoft/vscode-extension-samples/blob/5ae1f7787122812dcc84e37427ca90af5ee09f14/semantic-tokens-sample/vscode.proposed.d.ts#L71 /// public ImmutableArray Data { get; init; } + + public SemanticTokensPartialResult() {} + + internal SemanticTokensPartialResult(SemanticTokens? result) => Data = result?.Data ?? ImmutableArray.Empty; } @@ -209,6 +215,10 @@ public record SemanticTokensDeltaPartialResult /// https://github.com/microsoft/vscode-extension-samples/blob/5ae1f7787122812dcc84e37427ca90af5ee09f14/semantic-tokens-sample/vscode.proposed.d.ts#L71 /// public Container Edits { get; init; } + public SemanticTokensDeltaPartialResult() {} + + internal SemanticTokensDeltaPartialResult(SemanticTokensDelta? result) => Edits = result?.Edits ?? new Container(); + } /// @@ -243,6 +253,7 @@ public record SemanticTokensEdit [Optional] public ImmutableArray? Data { get; init; } = ImmutableArray.Empty; } + [JsonConverter(typeof(SemanticTokensFullOrDeltaConverter))] public record SemanticTokensFullOrDelta { @@ -280,7 +291,7 @@ public SemanticTokensFullOrDelta(SemanticTokensFullOrDeltaPartialResult partialR public SemanticTokens? Full { get; init; } public bool IsDelta => Delta != null; - public SemanticTokensDelta? Delta { get;init; } + public SemanticTokensDelta? Delta { get; init; } [return: NotNullIfNotNull("semanticTokensDelta")] public static SemanticTokensFullOrDelta? From(SemanticTokensDelta? semanticTokensDelta) => semanticTokensDelta switch { @@ -319,6 +330,7 @@ public SemanticTokensFullOrDelta(SemanticTokensFullOrDeltaPartialResult partialR _ => null }; } + [JsonConverter(typeof(SemanticTokensFullOrDeltaPartialResultConverter))] public record SemanticTokensFullOrDeltaPartialResult { @@ -338,6 +350,19 @@ SemanticTokensDeltaPartialResult delta Delta = delta; } + public SemanticTokensFullOrDeltaPartialResult(SemanticTokensFullOrDelta delta) + { + if (delta.IsFull) + { + Full = new SemanticTokensPartialResult(delta.Full); + } + + if (delta.IsDelta) + { + Delta = new SemanticTokensDeltaPartialResult(delta.Delta); + } + } + public bool IsDelta => Delta != null; public SemanticTokensDeltaPartialResult? Delta { get; } @@ -535,6 +560,7 @@ public readonly partial struct SemanticTokenType public static SemanticTokenType Event { get; } = new SemanticTokenType("event"); public static SemanticTokenType EnumMember { get; } = new SemanticTokenType("enumMember"); } + [RegistrationName(TextDocumentNames.SemanticTokensRegistration)] [GenerateRegistrationOptions(nameof(ServerCapabilities.SemanticTokensProvider))] [RegistrationOptionsConverter(typeof(SemanticTokensRegistrationOptionsConverter))] @@ -731,6 +757,7 @@ public abstract class SemanticTokensHandlerBase : protected abstract Task Tokenize(SemanticTokensBuilder builder, ITextDocumentIdentifierParams identifier, CancellationToken cancellationToken); protected abstract Task GetSemanticTokensDocument(ITextDocumentIdentifierParams @params, CancellationToken cancellationToken); } + public static partial class SemanticTokensExtensions { private static SemanticTokensRegistrationOptions RegistrationOptionsFactory(SemanticTokensCapability capability, ClientCapabilities clientCapabilities) @@ -739,7 +766,7 @@ private static SemanticTokensRegistrationOptions RegistrationOptionsFactory(Sema Full = new SemanticTokensCapabilityRequestFull() }; registrationOptions.Range ??= new SemanticTokensCapabilityRequestRange(); - if (registrationOptions is { Full: { IsValue: true, Value: {} } }) + if (registrationOptions is { Full: { IsValue: true, Value: { } } }) { registrationOptions.Full.Value.Delta = true; } @@ -827,7 +854,9 @@ protected override Task Tokenize(SemanticTokensBuilder builder, ITextDocumentIde protected override Task GetSemanticTokensDocument(ITextDocumentIdentifierParams @params, CancellationToken cancellationToken) => _getSemanticTokensDocument(@params, Capability, cancellationToken); - protected internal override SemanticTokensRegistrationOptions CreateRegistrationOptions(SemanticTokensCapability capability, ClientCapabilities clientCapabilities) => + protected internal override SemanticTokensRegistrationOptions CreateRegistrationOptions( + SemanticTokensCapability capability, ClientCapabilities clientCapabilities + ) => _registrationOptionsFactory(capability, clientCapabilities); } @@ -836,10 +865,13 @@ protected internal override SemanticTokensRegistrationOptions CreateRegistration SemanticTokensParams @params, CancellationToken cancellationToken = default ) => mediator.ProgressManager.MonitorUntil( - @params, (partial, result) => new SemanticTokens { + @params, + (partial, result) => new SemanticTokens { Data = partial.Data, ResultId = result?.ResultId - }, cancellationToken + }, + tokens => new SemanticTokensPartialResult(tokens)!, + cancellationToken ); public static IRequestProgressObservable RequestSemanticTokensDelta( @@ -876,10 +908,13 @@ protected internal override SemanticTokensRegistrationOptions CreateRegistration SemanticTokensRangeParams @params, CancellationToken cancellationToken = default ) => mediator.ProgressManager.MonitorUntil( - @params, (partial, result) => new SemanticTokens { + @params, + (partial, result) => new SemanticTokens { Data = partial.Data, ResultId = result?.ResultId - }, cancellationToken + }, + tokens => new SemanticTokensPartialResult(tokens)!, + cancellationToken ); } } diff --git a/src/Protocol/Features/ProgressFeature.cs b/src/Protocol/Features/ProgressFeature.cs index 9807c1d15..37e3038c0 100644 --- a/src/Protocol/Features/ProgressFeature.cs +++ b/src/Protocol/Features/ProgressFeature.cs @@ -19,7 +19,7 @@ namespace OmniSharp.Extensions.LanguageServer.Protocol { namespace Models { - [Parallel] + [Serial] [Method(GeneralNames.Progress, Direction.Bidirectional)] [GenerateHandler("OmniSharp.Extensions.LanguageServer.Protocol"), GenerateHandlerMethods, GenerateRequestMethods(typeof(IGeneralLanguageClient), typeof(ILanguageClient), typeof(IGeneralLanguageServer), typeof(ILanguageServer))] @@ -103,7 +103,10 @@ public string? String public static partial class ProgressExtensions { public static IRequestProgressObservable RequestProgress( - this ILanguageProtocolProxy requestRouter, IPartialItemRequest @params, Func factory, CancellationToken cancellationToken = default + this ILanguageProtocolProxy requestRouter, + IPartialItemRequest @params, + Func factory, + CancellationToken cancellationToken = default ) { @params.SetPartialResultToken(new ProgressToken(Guid.NewGuid().ToString())); diff --git a/src/Protocol/Features/Window/WorkDoneProgressFeature.cs b/src/Protocol/Features/Window/WorkDoneProgressFeature.cs index 9e51d0ce4..d53fc071c 100644 --- a/src/Protocol/Features/Window/WorkDoneProgressFeature.cs +++ b/src/Protocol/Features/Window/WorkDoneProgressFeature.cs @@ -11,7 +11,7 @@ namespace OmniSharp.Extensions.LanguageServer.Protocol { namespace Models { - [Parallel] + [Serial] [Method(WindowNames.WorkDoneProgressCreate, Direction.ServerToClient)] [GenerateHandler("OmniSharp.Extensions.LanguageServer.Protocol.Window"), GenerateHandlerMethods, GenerateRequestMethods(typeof(IWindowLanguageServer), typeof(ILanguageServer))] @@ -23,7 +23,7 @@ public partial record WorkDoneProgressCreateParams : IRequest public ProgressToken? Token { get; init; } } - [Parallel] + [Serial] [Method(WindowNames.WorkDoneProgressCancel, Direction.ClientToServer)] [GenerateHandler("OmniSharp.Extensions.LanguageServer.Protocol.Window"), GenerateHandlerMethods, GenerateRequestMethods(typeof(IWindowLanguageClient), typeof(ILanguageClient))] diff --git a/src/Protocol/LanguageProtocolDelegatingHandlers.cs b/src/Protocol/LanguageProtocolDelegatingHandlers.cs index 13251bc88..1b6386b75 100644 --- a/src/Protocol/LanguageProtocolDelegatingHandlers.cs +++ b/src/Protocol/LanguageProtocolDelegatingHandlers.cs @@ -29,14 +29,19 @@ public sealed class Request private readonly Guid _id; Guid ICanBeIdentifiedHandler.Id => _id; - public Request(Guid id, Func> handler, RegistrationOptionsDelegate registrationOptionsFactory) + public Request( + Guid id, Func> handler, + RegistrationOptionsDelegate registrationOptionsFactory + ) { _id = id; _handler = handler; _registrationOptionsFactory = registrationOptionsFactory; } - public Request(Func> handler, RegistrationOptionsDelegate registrationOptionsFactory) : + public Request( + Func> handler, RegistrationOptionsDelegate registrationOptionsFactory + ) : this(Guid.Empty, handler, registrationOptionsFactory) { } @@ -44,7 +49,8 @@ public Request(Func> hand Task IRequestHandler.Handle(TParams request, CancellationToken cancellationToken) => _handler(request, Capability, cancellationToken); - protected internal override TRegistrationOptions CreateRegistrationOptions(TCapability capability, ClientCapabilities clientCapabilities) => _registrationOptionsFactory(capability, clientCapabilities); + protected internal override TRegistrationOptions CreateRegistrationOptions(TCapability capability, ClientCapabilities clientCapabilities) => + _registrationOptionsFactory(capability, clientCapabilities); } public sealed class CanBeResolved : @@ -56,11 +62,14 @@ public sealed class CanBeResolved : where TCapability : ICapability { private readonly Func> _resolveHandler; - private readonly RegistrationOptionsDelegate _registrationOptionsFactory; + private readonly RegistrationOptionsDelegate _registrationOptionsFactory; private readonly Guid _id; Guid ICanBeIdentifiedHandler.Id => _id; - public CanBeResolved(Guid id, Func> resolveHandler, RegistrationOptionsDelegate registrationOptionsFactory) + public CanBeResolved( + Guid id, Func> resolveHandler, + RegistrationOptionsDelegate registrationOptionsFactory + ) { _resolveHandler = resolveHandler; _registrationOptionsFactory = registrationOptionsFactory; @@ -69,7 +78,8 @@ public CanBeResolved(Guid id, Func IRequestHandler.Handle(TItem request, CancellationToken cancellationToken) => _resolveHandler(request, Capability, cancellationToken); - protected internal override TRegistrationOptions CreateRegistrationOptions(TCapability capability, ClientCapabilities clientCapabilities) => _registrationOptionsFactory(capability, clientCapabilities); + protected internal override TRegistrationOptions CreateRegistrationOptions(TCapability capability, ClientCapabilities clientCapabilities) => + _registrationOptionsFactory(capability, clientCapabilities); } public sealed class CanBeResolved : @@ -109,15 +119,17 @@ public sealed class Request : private readonly Guid _id; Guid ICanBeIdentifiedHandler.Id => _id; - public Request(Guid id, Func handler, RegistrationOptionsDelegate registrationOptionsFactory) + public Request( + Guid id, Func handler, RegistrationOptionsDelegate registrationOptionsFactory + ) { _id = id; _handler = handler; _registrationOptionsFactory = registrationOptionsFactory; } - public Request(Func handler, RegistrationOptionsDelegate registrationOptionsFactory): - this(Guid.Empty, handler, registrationOptionsFactory) + public Request(Func handler, RegistrationOptionsDelegate registrationOptionsFactory) : + this(Guid.Empty, handler, registrationOptionsFactory) { } @@ -128,7 +140,8 @@ async Task IRequestHandler.Handle(TParams request, Cancella return Unit.Value; } - protected internal override TRegistrationOptions CreateRegistrationOptions(TCapability capability, ClientCapabilities clientCapabilities) => _registrationOptionsFactory(capability, clientCapabilities); + protected internal override TRegistrationOptions CreateRegistrationOptions(TCapability capability, ClientCapabilities clientCapabilities) => + _registrationOptionsFactory(capability, clientCapabilities); } public sealed class RequestRegistration : @@ -148,7 +161,9 @@ public RequestRegistration(Func> handl { } - public RequestRegistration(Guid id, Func> handler, RegistrationOptionsDelegate registrationOptionsFactory) + public RequestRegistration( + Guid id, Func> handler, RegistrationOptionsDelegate registrationOptionsFactory + ) { _id = id; _handler = handler; @@ -180,7 +195,7 @@ public RequestRegistration(Guid id, Func handl _registrationOptionsFactory = registrationOptionsFactory; } - public RequestRegistration(Func handler, RegistrationOptionsDelegate registrationOptionsFactory): + public RequestRegistration(Func handler, RegistrationOptionsDelegate registrationOptionsFactory) : this(Guid.Empty, handler, registrationOptionsFactory) { } @@ -270,7 +285,8 @@ public sealed class PartialResult _id; public PartialResult( - Guid id, Action, TCapability, CancellationToken> handler, RegistrationOptionsDelegate registrationOptionsFactory, IProgressManager progressManager, + Guid id, Action, TCapability, CancellationToken> handler, + RegistrationOptionsDelegate registrationOptionsFactory, IProgressManager progressManager, Func factory ) { @@ -282,9 +298,10 @@ public PartialResult( } public PartialResult( - Action, TCapability, CancellationToken> handler, RegistrationOptionsDelegate registrationOptionsFactory, IProgressManager progressManager, + Action, TCapability, CancellationToken> handler, + RegistrationOptionsDelegate registrationOptionsFactory, IProgressManager progressManager, Func factory - ): + ) : this(Guid.Empty, handler, registrationOptionsFactory, progressManager, factory) { } @@ -303,13 +320,18 @@ CancellationToken cancellationToken } var subject = new AsyncSubject(); + var task = subject + .Select(_factory) + .ToTask(cancellationToken, _progressManager.Scheduler) + .ConfigureAwait(false); // in the event nothing is emitted... subject.OnNext(default!); _handler(request, subject, Capability, cancellationToken); - return await subject.Select(_factory).ToTask(cancellationToken).ConfigureAwait(false); + return await task; } - protected internal override TRegistrationOptions CreateRegistrationOptions(TCapability capability, ClientCapabilities clientCapabilities) => _registrationOptionsFactory(capability, clientCapabilities); + protected internal override TRegistrationOptions CreateRegistrationOptions(TCapability capability, ClientCapabilities clientCapabilities) => + _registrationOptionsFactory(capability, clientCapabilities); } public sealed class PartialResult : @@ -329,14 +351,17 @@ public sealed class PartialResult _id; public PartialResult( - Action, CancellationToken> handler, RegistrationOptionsDelegate registrationOptionsFactory, IProgressManager progressManager, + Action, CancellationToken> handler, RegistrationOptionsDelegate registrationOptionsFactory, + IProgressManager progressManager, Func factory ) : this(Guid.Empty, handler, registrationOptionsFactory, progressManager, factory) { } + public PartialResult( - Guid id, Action, CancellationToken> handler, RegistrationOptionsDelegate registrationOptionsFactory, IProgressManager progressManager, + Guid id, Action, CancellationToken> handler, RegistrationOptionsDelegate registrationOptionsFactory, + IProgressManager progressManager, Func factory ) { @@ -358,10 +383,14 @@ public PartialResult( } var subject = new AsyncSubject(); + var task = subject + .Select(_factory) + .ToTask(cancellationToken, _progressManager.Scheduler) + .ConfigureAwait(false); // in the event nothing is emitted... subject.OnNext(default!); _handler(request, subject, cancellationToken); - return await subject.Select(_factory).ToTask(cancellationToken).ConfigureAwait(false); + return await task; } protected override TRegistrationOptions CreateRegistrationOptions(ClientCapabilities clientCapabilities) => _registrationOptionsFactory(clientCapabilities); @@ -410,10 +439,14 @@ public PartialResultCapability( } var subject = new AsyncSubject(); + var task = subject + .Select(_factory) + .ToTask(cancellationToken, _progressManager.Scheduler) + .ConfigureAwait(false); // in the event nothing is emitted... subject.OnNext(default!); _handler(request, Capability, subject, cancellationToken); - return await subject.Select(_factory).ToTask(cancellationToken).ConfigureAwait(false); + return await task; } } @@ -454,10 +487,14 @@ public PartialResult(Guid id, Action, CancellationToke } var subject = new AsyncSubject(); + var task = subject + .Select(_factory) + .ToTask(cancellationToken, _progressManager.Scheduler) + .ConfigureAwait(false); // in the event nothing is emitted... subject.OnNext(default!); _handler(request, subject, cancellationToken); - return await subject.Select(_factory).ToTask(cancellationToken).ConfigureAwait(false); + return await task; } } @@ -479,7 +516,8 @@ public sealed class PartialResults _id; public PartialResults( - Action>, TCapability, CancellationToken> handler, RegistrationOptionsDelegate registrationOptionsFactory, IProgressManager progressManager, + Action>, TCapability, CancellationToken> handler, + RegistrationOptionsDelegate registrationOptionsFactory, IProgressManager progressManager, Func, TResponse?> factory ) : this(Guid.Empty, handler, registrationOptionsFactory, progressManager, factory) @@ -487,7 +525,8 @@ public PartialResults( } public PartialResults( - Guid id, Action>, TCapability, CancellationToken> handler, RegistrationOptionsDelegate registrationOptionsFactory, + Guid id, Action>, TCapability, CancellationToken> handler, + RegistrationOptionsDelegate registrationOptionsFactory, IProgressManager progressManager, Func, TResponse?> factory ) { @@ -509,19 +548,22 @@ public PartialResults( } var subject = new Subject>(); - var task = subject.Aggregate( - new List(), (acc, items) => { - acc.AddRange(items); - return acc; - } - ) - .ToTask(cancellationToken); + var task = subject + .Aggregate( + new List(), (acc, items) => { + acc.AddRange(items); + return acc; + } + ) + .Select(_factory) + .ToTask(cancellationToken, _progressManager.Scheduler) + .ConfigureAwait(false); _handler(request, subject, Capability, cancellationToken); - var result = _factory(await task.ConfigureAwait(false)); - return result; + return await task; } - protected internal override TRegistrationOptions CreateRegistrationOptions(TCapability capability, ClientCapabilities clientCapabilities) => _registrationOptionsFactory(capability, clientCapabilities); + protected internal override TRegistrationOptions CreateRegistrationOptions(TCapability capability, ClientCapabilities clientCapabilities) => + _registrationOptionsFactory(capability, clientCapabilities); } public sealed class PartialResults : @@ -541,7 +583,8 @@ public sealed class PartialResults _id; public PartialResults( - Action>, CancellationToken> handler, RegistrationOptionsDelegate registrationOptionsFactory, IProgressManager progressManager, + Action>, CancellationToken> handler, RegistrationOptionsDelegate registrationOptionsFactory, + IProgressManager progressManager, Func, TResponse?> factory ) : this(Guid.Empty, handler, registrationOptionsFactory, progressManager, factory) @@ -549,7 +592,8 @@ public PartialResults( } public PartialResults( - Guid id, Action>, CancellationToken> handler, RegistrationOptionsDelegate registrationOptionsFactory, IProgressManager progressManager, + Guid id, Action>, CancellationToken> handler, RegistrationOptionsDelegate registrationOptionsFactory, + IProgressManager progressManager, Func, TResponse?> factory ) { @@ -571,16 +615,18 @@ public PartialResults( } var subject = new Subject>(); - var task = subject.Aggregate( - new List(), (acc, items) => { - acc.AddRange(items); - return acc; - } - ) - .ToTask(cancellationToken); + var task = subject + .Aggregate( + new List(), (acc, items) => { + acc.AddRange(items); + return acc; + } + ) + .Select(_factory) + .ToTask(cancellationToken, _progressManager.Scheduler) + .ConfigureAwait(false); _handler(request, subject, cancellationToken); - var result = _factory(await task.ConfigureAwait(false)); - return result; + return await task; } protected override TRegistrationOptions CreateRegistrationOptions(ClientCapabilities clientCapabilities) => _registrationOptionsFactory(clientCapabilities); @@ -631,16 +677,18 @@ public PartialResultsCapability( } var subject = new Subject>(); - var task = subject.Aggregate( - new List(), (acc, items) => { - acc.AddRange(items); - return acc; - } - ) - .ToTask(cancellationToken); + var task = subject + .Aggregate( + new List(), (acc, items) => { + acc.AddRange(items); + return acc; + } + ) + .Select(_factory) + .ToTask(cancellationToken, _progressManager.Scheduler) + .ConfigureAwait(false); _handler(request, subject, Capability, cancellationToken); - var result = _factory(await task.ConfigureAwait(false)); - return result; + return await task; } } @@ -688,16 +736,18 @@ public PartialResults( } var subject = new Subject>(); - var task = subject.Aggregate( - new List(), (acc, items) => { - acc.AddRange(items); - return acc; - } - ) - .ToTask(cancellationToken); + var task = subject + .Aggregate( + new List(), (acc, items) => { + acc.AddRange(items); + return acc; + } + ) + .Select(_factory) + .ToTask(cancellationToken, _progressManager.Scheduler) + .ConfigureAwait(false); _handler(request, subject, cancellationToken); - var result = _factory(await task.ConfigureAwait(false)); - return result; + return await task; } } @@ -714,14 +764,18 @@ public sealed class Notification : private readonly Guid _id; Guid ICanBeIdentifiedHandler.Id => _id; - public Notification(Guid id, Func handler, RegistrationOptionsDelegate registrationOptionsFactory) + public Notification( + Guid id, Func handler, RegistrationOptionsDelegate registrationOptionsFactory + ) { _id = id; _handler = handler; _registrationOptionsFactory = registrationOptionsFactory; } - public Notification(Func handler, RegistrationOptionsDelegate registrationOptionsFactory) : + public Notification( + Func handler, RegistrationOptionsDelegate registrationOptionsFactory + ) : this(Guid.Empty, handler, registrationOptionsFactory) { } @@ -732,7 +786,8 @@ async Task IRequestHandler.Handle(TParams request, Cancella return Unit.Value; } - protected internal override TRegistrationOptions CreateRegistrationOptions(TCapability capability, ClientCapabilities clientCapabilities) => _registrationOptionsFactory(capability, clientCapabilities); + protected internal override TRegistrationOptions CreateRegistrationOptions(TCapability capability, ClientCapabilities clientCapabilities) => + _registrationOptionsFactory(capability, clientCapabilities); } public sealed class Notification : @@ -807,6 +862,7 @@ public TypedPartialObserver(IObserver> results, Func fact _results = results; _factory = factory; } + public void OnCompleted() => _results.OnCompleted(); public void OnError(Exception error) => _results.OnError(error); diff --git a/src/Protocol/LanguageProtocolEventingHelper.cs b/src/Protocol/LanguageProtocolEventingHelper.cs index 0a3898eb3..812984951 100644 --- a/src/Protocol/LanguageProtocolEventingHelper.cs +++ b/src/Protocol/LanguageProtocolEventingHelper.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reactive.Concurrency; using System.Reactive.Linq; using System.Reactive.Threading.Tasks; using System.Threading; @@ -16,6 +17,7 @@ public static Task Run( IEnumerable handlers, Func executeHandler, int? concurrency, + IScheduler scheduler, CancellationToken cancellationToken ) { @@ -28,13 +30,13 @@ CancellationToken cancellationToken { return events.Merge(concurrency.Value) .LastOrDefaultAsync() - .ToTask(cancellationToken); + .ToTask(cancellationToken, scheduler); } return events .Merge() .LastOrDefaultAsync() - .ToTask(cancellationToken); + .ToTask(cancellationToken, scheduler); } } } diff --git a/src/Protocol/Progress/IProgressManager.cs b/src/Protocol/Progress/IProgressManager.cs index df6a0df52..f46219cc3 100644 --- a/src/Protocol/Progress/IProgressManager.cs +++ b/src/Protocol/Progress/IProgressManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Reactive.Concurrency; using System.Threading; using Newtonsoft.Json.Linq; using OmniSharp.Extensions.LanguageServer.Protocol.Models; @@ -14,9 +15,25 @@ public interface IProgressManager : IProgressHandler IRequestProgressObservable MonitorUntil( IPartialItemRequest request, Func factory, + Func reverseFactory, CancellationToken cancellationToken ); + IRequestProgressObservable MonitorUntil( + IPartialItemRequest request, + Func factory, + Func reverseFactory, + CancellationToken cancellationToken + ); + + [Obsolete("Method may not work correctly when subscribing to the result, use overload with the reverse factory parameter")] + IRequestProgressObservable MonitorUntil( + IPartialItemRequest request, + Func factory, + CancellationToken cancellationToken + ); + + [Obsolete("Method may not work correctly when subscribing to the result, use overload with the reverse factory parameter")] IRequestProgressObservable MonitorUntil( IPartialItemRequest request, Func factory, @@ -39,5 +56,6 @@ CancellationToken cancellationToken IProgressObserver> For(IPartialItemsRequest request, CancellationToken cancellationToken) where TResponse : IEnumerable?; + IScheduler Scheduler { get; } } } diff --git a/src/Protocol/Progress/RequestProgressObservable.cs b/src/Protocol/Progress/PartialItemRequestProgressObservable.cs similarity index 53% rename from src/Protocol/Progress/RequestProgressObservable.cs rename to src/Protocol/Progress/PartialItemRequestProgressObservable.cs index 2295753d0..439e672b3 100644 --- a/src/Protocol/Progress/RequestProgressObservable.cs +++ b/src/Protocol/Progress/PartialItemRequestProgressObservable.cs @@ -1,4 +1,6 @@ using System; +using System.Reactive; +using System.Reactive.Concurrency; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reactive.Subjects; @@ -6,52 +8,56 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.LanguageServer.Protocol.Models; namespace OmniSharp.Extensions.LanguageServer.Protocol.Progress { - internal class RequestProgressObservable : IRequestProgressObservable, IObserver + internal class PartialItemRequestProgressObservable : IRequestProgressObservable, IObserver { private readonly ISerializer _serializer; private readonly ReplaySubject _dataSubject; private readonly CompositeDisposable _disposable; private readonly Task _task; + private bool _receivedPartialData; - public RequestProgressObservable( + public PartialItemRequestProgressObservable( ISerializer serializer, ProgressToken token, IObservable requestResult, Func factory, + Func reverseFactory, CancellationToken cancellationToken, - Action disposal + Action onCompleteAction ) { _serializer = serializer; - _dataSubject = new ReplaySubject(1); - var request = requestResult - .Do(_ => { }, OnError, OnCompleted) - .Replay(1); - _disposable = new CompositeDisposable { - request.Connect(), - Disposable.Create(disposal) - }; - - _task = _dataSubject - .ForkJoin(requestResult, factory) - .ToTask(cancellationToken); -#pragma warning disable VSTHRD105 -#pragma warning disable VSTHRD110 - _task.ContinueWith(_ => Dispose()); -#pragma warning restore VSTHRD110 -#pragma warning restore VSTHRD105 + _dataSubject = new ReplaySubject(1, Scheduler.Immediate); + _disposable = new CompositeDisposable() { _dataSubject }; + _task = Observable.Create( + observer => new CompositeDisposable() { + _dataSubject + .ForkJoin( + requestResult + .Do( + _ => { + if (_receivedPartialData) return; + _dataSubject.OnNext(reverseFactory(_)); + }, + _dataSubject.OnError, + _dataSubject.OnCompleted + ), + factory + ) + .Subscribe(observer), + Disposable.Create(onCompleteAction) + } + ) + .ToTask(cancellationToken); ProgressToken = token; - if (_dataSubject is IDisposable disposable) - { - _disposable.Add(disposable); - } } public ProgressToken ProgressToken { get; } @@ -75,6 +81,7 @@ private void OnError(Exception error) public void OnNext(JToken value) { if (_dataSubject.IsDisposed) return; + _receivedPartialData = true; _dataSubject.OnNext(value.ToObject(_serializer.JsonSerializer)); } @@ -84,7 +91,7 @@ public void Dispose() _disposable.Dispose(); } - public IDisposable Subscribe(IObserver observer) => _disposable.IsDisposed ? Disposable.Empty : _dataSubject.Subscribe(observer); + public IDisposable Subscribe(IObserver observer) => _dataSubject.Subscribe(observer); #pragma warning disable VSTHRD003 public Task AsTask() => _task; diff --git a/src/Protocol/Progress/PartialItemsRequestProgressObservable.cs b/src/Protocol/Progress/PartialItemsRequestProgressObservable.cs index be654e680..79a4fa040 100644 --- a/src/Protocol/Progress/PartialItemsRequestProgressObservable.cs +++ b/src/Protocol/Progress/PartialItemsRequestProgressObservable.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reactive; +using System.Reactive.Concurrency; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reactive.Subjects; @@ -8,6 +10,7 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.LanguageServer.Protocol.Models; @@ -21,6 +24,7 @@ internal class PartialItemsRequestProgressObservable : IRequestP private readonly ReplaySubject> _dataSubject; private readonly CompositeDisposable _disposable; private readonly Task _task; + private bool _receivedPartialData; public PartialItemsRequestProgressObservable( ISerializer serializer, @@ -28,48 +32,39 @@ public PartialItemsRequestProgressObservable( IObservable requestResult, Func, TResult> factory, CancellationToken cancellationToken, - Action disposal + Action onCompleteAction ) { _serializer = serializer; - _dataSubject = new ReplaySubject>(int.MaxValue); - var request = requestResult - .Do( - // this should be fine as long as the other side is spec compliant (requests cannot return new results) so this should be null or empty - result => _dataSubject.OnNext(result ?? Enumerable.Empty()), - OnError, - OnCompleted - ) - .Replay(1); - _disposable = new CompositeDisposable { - request.Connect(), - Disposable.Create(disposal) - }; - - _task = _dataSubject - .Scan( - new List(), - (acc, data) => { - acc.AddRange(data); - return acc; - } - ) - .StartWith(new List()) - .Select(factory) - .ForkJoin(request, (items, result) => items?.Count() > result?.Count() ? items : result) - .ToTask(cancellationToken); - -#pragma warning disable VSTHRD105 -#pragma warning disable VSTHRD110 - _task.ContinueWith(_ => Dispose()); -#pragma warning restore VSTHRD110 -#pragma warning restore VSTHRD105 + _dataSubject = new ReplaySubject>(int.MaxValue, Scheduler.Immediate); + _disposable = new CompositeDisposable() { _dataSubject }; + _task = Observable.Create( + observer => new CompositeDisposable() { + _dataSubject + .Aggregate( + new List(), + (acc, data) => { + acc.AddRange(data); + return acc; + } + ) + .Select(factory) + .ForkJoin( + requestResult + .Do( + result => _dataSubject.OnNext(result ?? Enumerable.Empty()), + _dataSubject.OnError, + _dataSubject.OnCompleted + ), + (items, result) => items?.Count() > result?.Count() ? items : result + ) + .Subscribe(observer), + Disposable.Create(onCompleteAction) + } + ) + .ToTask(cancellationToken); ProgressToken = token; - if (_dataSubject is IDisposable disposable) - { - _disposable.Add(disposable); - } } public ProgressToken ProgressToken { get; } @@ -93,6 +88,7 @@ private void OnError(Exception error) public void OnNext(JToken value) { if (_dataSubject.IsDisposed) return; + _receivedPartialData = true; _dataSubject.OnNext(value.ToObject(_serializer.JsonSerializer)); } @@ -102,7 +98,8 @@ public void Dispose() _disposable.Dispose(); } - public IDisposable Subscribe(IObserver> observer) => _disposable.IsDisposed ? Disposable.Empty : _dataSubject.Subscribe(observer); +// public IDisposable Subscribe(IObserver> observer) => _disposable.IsDisposed ? Disposable.Empty : _dataSubject.Subscribe(observer); + public IDisposable Subscribe(IObserver> observer) => _dataSubject.Subscribe(observer); #pragma warning disable VSTHRD003 public Task AsTask() => _task; @@ -118,14 +115,15 @@ public PartialItemsRequestProgressObservable( IObservable?> requestResult, Func, Container?> factory, CancellationToken cancellationToken, - Action disposal + Action onCompleteAction, + ILogger logger ) : base( serializer, token, requestResult, factory, cancellationToken, - disposal + onCompleteAction ) { } diff --git a/src/Protocol/Progress/ProgressManager.cs b/src/Protocol/Progress/ProgressManager.cs index c4885c4e2..c57fb182a 100644 --- a/src/Protocol/Progress/ProgressManager.cs +++ b/src/Protocol/Progress/ProgressManager.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Reactive.Concurrency; using System.Reactive.Linq; using System.Threading; using System.Threading.Tasks; using MediatR; +using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.LanguageServer.Protocol.Models; @@ -16,6 +18,8 @@ internal class ProgressManager : IProgressManager { private readonly IResponseRouter _router; private readonly ISerializer _serializer; + private readonly IScheduler _scheduler; + private readonly Lazy> _logger; private readonly ConcurrentDictionary _activeObservers = new ConcurrentDictionary(EqualityComparer.Default); @@ -23,12 +27,16 @@ internal class ProgressManager : IProgressManager private readonly ConcurrentDictionary _activeObservables = new ConcurrentDictionary(EqualityComparer.Default); - public ProgressManager(IResponseRouter router, ISerializer serializer) + public ProgressManager(IResponseRouter router, ISerializer serializer, IScheduler scheduler, Lazy> logger) { _router = router; _serializer = serializer; + _scheduler = scheduler; + _logger = logger; } + IScheduler IProgressManager.Scheduler => _scheduler; + Task IRequestHandler.Handle(ProgressParams request, CancellationToken cancellationToken) { if (_activeObservables.TryGetValue(request.Token, out var observable) && observable is IObserver observer) @@ -55,7 +63,8 @@ public IProgressObservable Monitor(ProgressToken token, Func fa } public IRequestProgressObservable MonitorUntil( - IPartialItemRequest request, Func factory, + IPartialItemRequest request, + Func factory, CancellationToken cancellationToken ) { @@ -66,19 +75,22 @@ CancellationToken cancellationToken return observable; } - observable = new RequestProgressObservable( + observable = new PartialItemRequestProgressObservable( _serializer, request.PartialResultToken!, MakeRequest(request), (x, _) => factory(x), - cancellationToken, () => _activeObservables.TryRemove(request.PartialResultToken!, out _) + _ => default!, + cancellationToken, + () => _activeObservables.TryRemove(request.PartialResultToken!, out _) ); _activeObservables.TryAdd(request.PartialResultToken!, observable); return observable; } public IRequestProgressObservable MonitorUntil( - IPartialItemRequest request, Func factory, + IPartialItemRequest request, + Func factory, CancellationToken cancellationToken ) { @@ -88,9 +100,67 @@ CancellationToken cancellationToken return observable; } - observable = new RequestProgressObservable( - _serializer, request.PartialResultToken, MakeRequest(request), factory, - cancellationToken, () => _activeObservables.TryRemove(request.PartialResultToken, out _) + observable = new PartialItemRequestProgressObservable( + _serializer, + request.PartialResultToken, + MakeRequest(request), + factory, + _ => default!, + cancellationToken, + () => _activeObservables.TryRemove(request.PartialResultToken, out _) + ); + _activeObservables.TryAdd(request.PartialResultToken, observable); + return observable; + } + + public IRequestProgressObservable MonitorUntil( + IPartialItemRequest request, + Func factory, + Func reverseFactory, + CancellationToken cancellationToken + ) + { + request.SetPartialResultToken(); + if (_activeObservables.TryGetValue(request.PartialResultToken!, out var o) + && o is IRequestProgressObservable observable) + { + return observable; + } + + observable = new PartialItemRequestProgressObservable( + _serializer, + request.PartialResultToken!, + MakeRequest(request), + (x, _) => factory(x), + reverseFactory, + cancellationToken, + () => _activeObservables.TryRemove(request.PartialResultToken!, out _) + ); + _activeObservables.TryAdd(request.PartialResultToken!, observable); + return observable; + } + + public IRequestProgressObservable MonitorUntil( + IPartialItemRequest request, + Func factory, + Func reverseFactory, + CancellationToken cancellationToken + ) + { + request.SetPartialResultToken(); + if (_activeObservables.TryGetValue(request.PartialResultToken!, out var o) && o is IRequestProgressObservable observable) + { + return observable; + } + + observable = new PartialItemRequestProgressObservable( + _serializer, + request.PartialResultToken, + MakeRequest(request), + factory, + reverseFactory, + cancellationToken, + () => _activeObservables.TryRemove(request.PartialResultToken, out _) ); _activeObservables.TryAdd(request.PartialResultToken, observable); return observable; @@ -151,8 +221,13 @@ public IRequestProgressObservable MonitorUntil(IPartialItemsReques } observable = new PartialItemsRequestProgressObservable( - _serializer, request.PartialResultToken, MakeRequest(request), x => new Container(x), cancellationToken, - () => _activeObservables.TryRemove(request.PartialResultToken, out _) + _serializer, + request.PartialResultToken, + MakeRequest(request), + x => new Container(x), + cancellationToken, + () => _activeObservables.TryRemove(request.PartialResultToken, out _), + _logger.Value ); _activeObservables.TryAdd(request.PartialResultToken, observable); return observable; @@ -209,6 +284,7 @@ private IObservable MakeRequest(IRequest reques try { observer.OnNext(await _router.SendRequest(request, ct).ConfigureAwait(false)); + await Task.Yield(); observer.OnCompleted(); } catch (OperationCanceledException e) diff --git a/src/Protocol/Progress/ProgressObservable.cs b/src/Protocol/Progress/ProgressObservable.cs index 7cf81d7b3..645df8a34 100644 --- a/src/Protocol/Progress/ProgressObservable.cs +++ b/src/Protocol/Progress/ProgressObservable.cs @@ -1,4 +1,5 @@ using System; +using System.Reactive.Concurrency; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reactive.Subjects; @@ -16,7 +17,7 @@ internal class ProgressObservable : IProgressObservable, IObserver public ProgressObservable(ProgressToken token, Func factory, Action disposal) { _factory = factory; - _dataSubject = new ReplaySubject(1); + _dataSubject = new ReplaySubject(1, Scheduler.Immediate); _disposable = new CompositeDisposable { Disposable.Create(_dataSubject.OnCompleted), Disposable.Create(disposal) }; ProgressToken = token; diff --git a/src/Protocol/Protocol.csproj b/src/Protocol/Protocol.csproj index 90fd9171f..bedebe4d9 100644 --- a/src/Protocol/Protocol.csproj +++ b/src/Protocol/Protocol.csproj @@ -8,14 +8,14 @@ - - - + + + - - + + diff --git a/src/Server/Configuration/DidChangeConfigurationProvider.cs b/src/Server/Configuration/DidChangeConfigurationProvider.cs index 988f00d0f..143954809 100644 --- a/src/Server/Configuration/DidChangeConfigurationProvider.cs +++ b/src/Server/Configuration/DidChangeConfigurationProvider.cs @@ -2,6 +2,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Reactive.Concurrency; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reactive.Subjects; @@ -28,6 +29,7 @@ internal class DidChangeConfigurationProvider : ConfigurationProvider, IDidChang private readonly ILogger _logger; private readonly IWorkspaceLanguageServer _workspaceLanguageServer; private readonly ConfigurationConverter _configurationConverter; + private readonly IScheduler _scheduler; private DidChangeConfigurationCapability? _capability; private readonly ConfigurationRoot _configuration; private readonly CompositeDisposable _compositeDisposable = new CompositeDisposable(); @@ -40,12 +42,14 @@ public DidChangeConfigurationProvider( Action configurationBuilderAction, ILogger logger, IWorkspaceLanguageServer workspaceLanguageServer, - ConfigurationConverter configurationConverter + ConfigurationConverter configurationConverter, + IScheduler scheduler ) { _logger = logger; _workspaceLanguageServer = workspaceLanguageServer; _configurationConverter = configurationConverter; + _scheduler = scheduler; var builder = new ConfigurationBuilder() .Add(new DidChangeConfigurationSource(this)); configurationBuilderAction(builder); @@ -81,7 +85,7 @@ public Task Handle(DidChangeConfigurationParams request, CancellationToken Task IOnLanguageServerStarted.OnStarted(ILanguageServer server, CancellationToken cancellationToken) => GetWorkspaceConfigurationAsync(cancellationToken); - private Task GetWorkspaceConfigurationAsync(CancellationToken cancellationToken) => GetWorkspaceConfiguration().LastOrDefaultAsync().ToTask(cancellationToken); + private Task GetWorkspaceConfigurationAsync(CancellationToken cancellationToken) => GetWorkspaceConfiguration().LastOrDefaultAsync().ToTask(cancellationToken, _scheduler); private IObservable GetWorkspaceConfiguration() { @@ -225,7 +229,7 @@ public async Task GetScopedConfiguration(DocumentUri scope var data = await GetConfigurationFromClient(scopes.Select(z => new ConfigurationItem { Section = z.Section, ScopeUri = scopeUri })) .Select(z => ( z.scope.Section ?? string.Empty, z.settings )) .ToArray() - .ToTask(cancellationToken) + .ToTask(cancellationToken, _scheduler) .ConfigureAwait(false); var config = new ScopedConfiguration( diff --git a/src/Server/LanguageServer.Shutdown.cs b/src/Server/LanguageServer.Shutdown.cs index 2c8aab3fd..9f260c60d 100644 --- a/src/Server/LanguageServer.Shutdown.cs +++ b/src/Server/LanguageServer.Shutdown.cs @@ -18,8 +18,8 @@ public partial class LanguageServer : IExitHandler, IShutdownHandler public IObservable Shutdown => _shutdownSubject.AsObservable(); public IObservable Exit => _exitSubject.AsObservable(); - public Task WasShutDown => _shutdownSubject.ToTask(); - public Task WaitForExit => _exitSubject.ToTask(); + public Task WasShutDown => _shutdownSubject.ToTask(_scheduler); + public Task WaitForExit => _exitSubject.ToTask(_scheduler); #pragma warning disable VSTHRD100 public async void ForcefulShutdown() diff --git a/src/Server/LanguageServer.cs b/src/Server/LanguageServer.cs index 123958bdf..9e0aaae9b 100644 --- a/src/Server/LanguageServer.cs +++ b/src/Server/LanguageServer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reactive.Concurrency; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reactive.Subjects; @@ -50,6 +51,7 @@ public partial class LanguageServer : JsonRpcServerBase, ILanguageServer, ILangu private readonly IEnumerable _registrationOptionsConverters; private readonly InstanceHasStarted _instanceHasStarted; private readonly LanguageServerLoggingManager _languageServerLoggingManager; + private readonly IScheduler _scheduler; private readonly IEnumerable _startedDelegates; private readonly IEnumerable _startedHandlers; private readonly ISubject _initializeComplete = new AsyncSubject(); @@ -149,7 +151,8 @@ internal LanguageServer( IEnumerable initializedHandlers, IEnumerable registrationOptionsConverters, InstanceHasStarted instanceHasStarted, - LanguageServerLoggingManager languageServerLoggingManager + LanguageServerLoggingManager languageServerLoggingManager, + IScheduler scheduler ) : base(handlerCollection, responseRouter) { Configuration = configuration; @@ -182,6 +185,7 @@ LanguageServerLoggingManager languageServerLoggingManager _registrationOptionsConverters = registrationOptionsConverters; _instanceHasStarted = instanceHasStarted; _languageServerLoggingManager = languageServerLoggingManager; + _scheduler = scheduler; _concurrency = options.Value.Concurrency; _capabilityTypes = options.Value.UseAssemblyAttributeScanning @@ -242,7 +246,7 @@ public async Task Initialize(CancellationToken token) _connection.Open(); try { - _initializingTask = _initializeComplete.ToTask(token); + _initializingTask = _initializeComplete.ToTask(token, _scheduler); await _initializingTask.ConfigureAwait(false); await LanguageProtocolEventingHelper.Run( _startedDelegates, @@ -250,6 +254,7 @@ await LanguageProtocolEventingHelper.Run( _startedHandlers.Union(_collection.Select(z => z.Handler).OfType()), (handler, ct) => handler.OnStarted(this, ct), _concurrency, + _scheduler, token ).ConfigureAwait(false); @@ -281,6 +286,7 @@ await LanguageProtocolEventingHelper.Run( _initializeHandlers.Union(_collection.Select(z => z.Handler).OfType()), (handler, ct) => handler.OnInitialize(this, ClientSettings, ct), _concurrency, + _scheduler, token ).ConfigureAwait(false); @@ -302,6 +308,7 @@ await LanguageProtocolEventingHelper.Run( _initializedHandlers.Union(_collection.Select(z => z.Handler).OfType()), (handler, ct) => handler.OnInitialized(this, ClientSettings, result, ct), _concurrency, + _scheduler, token ).ConfigureAwait(false); @@ -493,7 +500,7 @@ private InitializeResult ReadServerCapabilities( public IObservable Start => _initializeComplete.AsObservable(); - public Task WasStarted => _initializeComplete.ToTask(); + public Task WasStarted => _initializeComplete.ToTask(_scheduler); public void Dispose() { diff --git a/src/Server/LanguageServerHelpers.cs b/src/Server/LanguageServerHelpers.cs index 77af39937..88797f1ae 100644 --- a/src/Server/LanguageServerHelpers.cs +++ b/src/Server/LanguageServerHelpers.cs @@ -127,30 +127,27 @@ IReadOnlyList descriptors ); } - return registrations.Distinct(new Registration.TextDocumentComparer()).ToArray(); - } - ) - .Where(z => z.Any()) - .SelectMany( - registrations => Observable.FromAsync(ct => client.RegisterCapability(new RegistrationParams { Registrations = registrations.ToArray() }, ct)), - (a, _) => a - ) - .Aggregate(Array.Empty(), (_, z) => z) - .Subscribe( - registrations => { + var distinctRegistrations = registrations.Distinct(new Registration.TextDocumentComparer()).ToArray(); disposable.Add( Disposable.Create( () => { client.UnregisterCapability( new UnregistrationParams { - Unregisterations = registrations.ToArray() + Unregisterations = distinctRegistrations } ).ToObservable().Subscribe(); } ) ); + return distinctRegistrations; } - ); + ) + .Where(z => z.Any()) + .SelectMany( + registrations => Observable.FromAsync(ct => client.RegisterCapability(new RegistrationParams { Registrations = registrations.ToArray() }, ct)), + (a, _) => a + ) + .Subscribe(); disposable.Add(result); return disposable; } diff --git a/src/Server/LanguageServerOptionsExtensions.cs b/src/Server/LanguageServerOptionsExtensions.cs index 6715949e6..91c0097fa 100644 --- a/src/Server/LanguageServerOptionsExtensions.cs +++ b/src/Server/LanguageServerOptionsExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Reactive.Concurrency; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -35,6 +36,54 @@ public static LanguageServerOptions WithServerInfo(this LanguageServerOptions op return options; } + /// + /// Sets both input and output schedulers to the same scheduler + /// + /// + /// + /// + public static LanguageServerOptions WithScheduler(this LanguageServerOptions options, IScheduler inputScheduler) + { + options.InputScheduler = options.OutputScheduler = options.DefaultScheduler = inputScheduler; + return options; + } + + /// + /// Sets the scheduler used during reading input + /// + /// + /// + /// + public static LanguageServerOptions WithInputScheduler(this LanguageServerOptions options, IScheduler inputScheduler) + { + options.InputScheduler = inputScheduler; + return options; + } + + /// + /// Sets the default scheduler to be used when scheduling other tasks + /// + /// + /// + /// + public static LanguageServerOptions WithDefaultScheduler(this LanguageServerOptions options, IScheduler defaultScheduler) + { + options.DefaultScheduler = defaultScheduler; + return options; + } + + /// + /// Sets the scheduler use during writing output + /// + /// + /// + /// + public static LanguageServerOptions WithOutputScheduler(this LanguageServerOptions options, IScheduler outputScheduler) + { + options.OutputScheduler = outputScheduler; + return options; + } + public static LanguageServerOptions OnInitialize(this LanguageServerOptions options, OnLanguageServerInitializeDelegate @delegate) { options.Services.AddSingleton(@delegate); diff --git a/src/Server/LanguageServerWorkspaceFolderManager.cs b/src/Server/LanguageServerWorkspaceFolderManager.cs index 951e4fde7..f56dce122 100644 --- a/src/Server/LanguageServerWorkspaceFolderManager.cs +++ b/src/Server/LanguageServerWorkspaceFolderManager.cs @@ -2,6 +2,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Reactive.Concurrency; using System.Reactive.Linq; using System.Reactive.Subjects; using System.Threading; @@ -27,7 +28,7 @@ public LanguageServerWorkspaceFolderManager(IWorkspaceLanguageServer server) { _server = server; _workspaceFolders = new ConcurrentDictionary(DocumentUri.Comparer); - _workspaceFoldersSubject = new ReplaySubject>(1); + _workspaceFoldersSubject = new ReplaySubject>(1, Scheduler.Immediate); _workspaceFoldersChangedSubject = new Subject(); } diff --git a/src/Testing/LanguageProtocolTestBase.cs b/src/Testing/LanguageProtocolTestBase.cs index d44f6761c..f8d844c60 100644 --- a/src/Testing/LanguageProtocolTestBase.cs +++ b/src/Testing/LanguageProtocolTestBase.cs @@ -49,6 +49,9 @@ Action serverOptionsAction .WithAssemblies(TestOptions.Assemblies) .ConfigureLogging(x => x.SetMinimumLevel(LogLevel.Trace)) .WithAssemblies(typeof(LanguageProtocolTestBase).Assembly, GetType().Assembly) + .WithInputScheduler(options.InputScheduler) + .WithOutputScheduler(options.OutputScheduler) + .WithDefaultScheduler(options.DefaultScheduler) .Services .AddTransient(typeof(IPipelineBehavior<,>), typeof(SettlePipeline<,>)) .AddSingleton(ClientEvents as IRequestSettler); @@ -64,6 +67,9 @@ Action serverOptionsAction .WithAssemblies(TestOptions.Assemblies) .ConfigureLogging(x => x.SetMinimumLevel(LogLevel.Trace)) .WithAssemblies(typeof(LanguageProtocolTestBase).Assembly, GetType().Assembly) + .WithInputScheduler(options.InputScheduler) + .WithOutputScheduler(options.OutputScheduler) + .WithDefaultScheduler(options.DefaultScheduler) .Services .AddTransient(typeof(IPipelineBehavior<,>), typeof(SettlePipeline<,>)) .AddSingleton(ServerEvents as IRequestSettler); diff --git a/src/Testing/LanguageServerTestBase.cs b/src/Testing/LanguageServerTestBase.cs index 61868007c..0343191e8 100644 --- a/src/Testing/LanguageServerTestBase.cs +++ b/src/Testing/LanguageServerTestBase.cs @@ -36,6 +36,9 @@ protected virtual ILanguageClient CreateClient(Action? cl .WithAssemblies(TestOptions.Assemblies) .WithAssemblies(typeof(LanguageProtocolTestBase).Assembly, GetType().Assembly) .ConfigureLogging(x => x.SetMinimumLevel(LogLevel.Trace)) + .WithInputScheduler(options.InputScheduler) + .WithOutputScheduler(options.OutputScheduler) + .WithDefaultScheduler(options.DefaultScheduler) .Services .AddTransient(typeof(IPipelineBehavior<,>), typeof(SettlePipeline<,>)) .AddSingleton(Events as IRequestSettler); diff --git a/test/Client.Tests/ClientTests.cs b/test/Client.Tests/ClientTests.cs index c4d583028..e435f3bb9 100644 --- a/test/Client.Tests/ClientTests.cs +++ b/test/Client.Tests/ClientTests.cs @@ -38,7 +38,7 @@ public ClientTests(ITestOutputHelper testOutput) /// /// Ensure that the language client can successfully request Hover information. /// - [FactWithSkipOn(SkipOnPlatform.Windows, DisplayName = "Language client can successfully request hover info")] + [Fact(DisplayName = "Language client can successfully request hover info")] public async Task Hover_Success() { const int line = 5; @@ -111,7 +111,7 @@ public async Task Hover_Success() /// /// Ensure that the language client can successfully request Completions. /// - [FactWithSkipOn(SkipOnPlatform.Windows, DisplayName = "Language client can successfully request completions")] + [Fact(DisplayName = "Language client can successfully request completions")] public async Task Completions_Success() { const int line = 5; @@ -215,7 +215,7 @@ public async Task Completions_Success() /// /// Ensure that the language client can successfully request SignatureHelp. /// - [FactWithSkipOn(SkipOnPlatform.Windows, DisplayName = "Language client can successfully request signature help")] + [Fact(DisplayName = "Language client can successfully request signature help")] public async Task SignatureHelp_Success() { const int line = 5; @@ -308,7 +308,7 @@ public async Task SignatureHelp_Success() /// /// Ensure that the language client can successfully request Definition. /// - [FactWithSkipOn(SkipOnPlatform.Windows, DisplayName = "Language client can successfully request definition")] + [Fact(DisplayName = "Language client can successfully request definition")] public async Task Definition_Success() { const int line = 5; @@ -384,7 +384,7 @@ public async Task Definition_Success() /// /// Ensure that the language client can successfully request DocumentHighlight. /// - [FactWithSkipOn(SkipOnPlatform.Windows, DisplayName = "Language client can successfully request document highlights")] + [Fact(DisplayName = "Language client can successfully request document highlights")] public async Task DocumentHighlights_Success() { const int line = 5; @@ -449,7 +449,7 @@ public async Task DocumentHighlights_Success() /// /// Ensure that the language client can successfully request DocumentHighlight. /// - [FactWithSkipOn(SkipOnPlatform.Windows, DisplayName = "Language client can successfully request document symbols")] + [Fact(DisplayName = "Language client can successfully request document symbols")] public async Task DocumentSymbols_DocumentSymbol_Success() { const int line = 5; @@ -534,7 +534,7 @@ public async Task DocumentSymbols_DocumentSymbol_Success() /// /// Ensure that the language client can successfully request FoldingRanges. /// - [FactWithSkipOn(SkipOnPlatform.Windows, DisplayName = "Language client can successfully request document folding ranges")] + [Fact(DisplayName = "Language client can successfully request document folding ranges")] public async Task FoldingRanges_Success() { var expectedDocumentPath = AbsoluteDocumentPath; @@ -594,7 +594,7 @@ public async Task FoldingRanges_Success() /// /// Ensure that the language client can successfully receive Diagnostics from the server. /// - [FactWithSkipOn(SkipOnPlatform.Windows, DisplayName = "Language client can successfully receive diagnostics")] + [Fact(DisplayName = "Language client can successfully receive diagnostics")] public async Task Diagnostics_Success() { var documentPath = AbsoluteDocumentPath; diff --git a/test/Dap.Tests/Integration/ConnectionAndDisconnectionTests.cs b/test/Dap.Tests/Integration/ConnectionAndDisconnectionTests.cs index e616f653c..6e58eba5f 100644 --- a/test/Dap.Tests/Integration/ConnectionAndDisconnectionTests.cs +++ b/test/Dap.Tests/Integration/ConnectionAndDisconnectionTests.cs @@ -22,7 +22,7 @@ public ConnectionAndDisconnectionTests(ITestOutputHelper outputHelper) : base( { } - [RetryFact] + [Fact] public async Task Server_Should_Stay_Alive_When_Requests_Throw_An_Exception() { var (client, _) = await Initialize(ConfigureClient, ConfigureServer); @@ -37,7 +37,7 @@ public async Task Server_Should_Stay_Alive_When_Requests_Throw_An_Exception() result.Should().BeTrue(); } - [RetryFact] + [Fact] public async Task Client_Should_Stay_Alive_When_Requests_Throw_An_Exception() { var (_, server) = await Initialize(ConfigureClient, ConfigureServer); @@ -52,7 +52,7 @@ public async Task Client_Should_Stay_Alive_When_Requests_Throw_An_Exception() result.Should().BeTrue(); } - [RetryFact] + [Fact] public async Task Server_Should_Support_Links() { var (client, _) = await Initialize(ConfigureClient, ConfigureServer); @@ -67,7 +67,7 @@ public async Task Server_Should_Support_Links() result.Should().BeTrue(); } - [RetryFact] + [Fact] public async Task Client_Should_Support_Links() { var (_, server) = await Initialize(ConfigureClient, ConfigureServer); diff --git a/test/Dap.Tests/Integration/CustomRequestsTests.cs b/test/Dap.Tests/Integration/CustomRequestsTests.cs index 5415e2b65..0d254ee7e 100644 --- a/test/Dap.Tests/Integration/CustomRequestsTests.cs +++ b/test/Dap.Tests/Integration/CustomRequestsTests.cs @@ -20,7 +20,7 @@ public CustomRequestsTests(ITestOutputHelper outputHelper) : base(new JsonRpcTes { } - [RetryFact] + [Fact] public async Task Should_Support_Custom_Attach_Request_Using_Base_Class() { var fake = Substitute.For>(); @@ -42,7 +42,7 @@ await client.Attach( request.ProcessId.Should().Be("4321"); } - [RetryFact] + [Fact] public async Task Should_Support_Custom_Attach_Request_Receiving_Regular_Request_Using_Base_Class() { var fake = Substitute.For(); @@ -64,7 +64,7 @@ await client.Attach( request.ExtensionData.Should().ContainKey("processId").And.Subject["processId"].Should().Be("4321"); } - [RetryFact] + [Fact] public async Task Should_Support_Custom_Attach_Request_Using_Extension_Data_Using_Base_Class() { var fake = Substitute.For>(); @@ -88,7 +88,7 @@ await client.Attach( request.ProcessId.Should().Be("4321"); } - [RetryFact] + [Fact] public async Task Should_Support_Custom_Launch_Request_Using_Base_Class() { var fake = Substitute.For>(); @@ -106,7 +106,7 @@ await client.Launch( request.Script.Should().Be("build.ps1"); } - [RetryFact] + [Fact] public async Task Should_Support_Custom_Launch_Request_Receiving_Regular_Request_Using_Base_Class() { var fake = Substitute.For(); @@ -124,7 +124,7 @@ await client.Launch( request.ExtensionData.Should().ContainKey("script").And.Subject["script"].Should().Be("build.ps1"); } - [RetryFact] + [Fact] public async Task Should_Support_Custom_Launch_Request_Using_Extension_Data_Base_Class() { var fake = Substitute.For>(); @@ -144,7 +144,7 @@ await client.Launch( request.Script.Should().Be("build.ps1"); } - [RetryFact] + [Fact] public async Task Should_Support_Custom_Attach_Request_Using_Delegate() { var fake = Substitute.For>>(); @@ -166,7 +166,7 @@ await client.Attach( request.ProcessId.Should().Be("4321"); } - [RetryFact] + [Fact] public async Task Should_Support_Custom_Attach_Request_Receiving_Regular_Request_Using_Delegate() { var fake = Substitute.For>>(); @@ -188,7 +188,7 @@ await client.Attach( request.ExtensionData.Should().ContainKey("processId").And.Subject["processId"].Should().Be("4321"); } - [RetryFact] + [Fact] public async Task Should_Support_Custom_Attach_Request_Using_Extension_Data_Using_Delegate() { var fake = Substitute.For>>(); @@ -212,7 +212,7 @@ await client.Attach( request.ProcessId.Should().Be("4321"); } - [RetryFact] + [Fact] public async Task Should_Support_Custom_Launch_Request_Using_Delegate() { var fake = Substitute.For>>(); @@ -230,7 +230,7 @@ await client.Launch( request.Script.Should().Be("build.ps1"); } - [RetryFact] + [Fact] public async Task Should_Support_Custom_Launch_Request_Receiving_Regular_Request_Using_Delegate() { var fake = Substitute.For>>(); @@ -248,7 +248,7 @@ await client.Launch( request.ExtensionData.Should().ContainKey("script").And.Subject["script"].Should().Be("build.ps1"); } - [RetryFact] + [Fact] public async Task Should_Support_Custom_Launch_Request_Using_Extension_Data_Using_Delegate() { var fake = Substitute.For>>(); diff --git a/test/Dap.Tests/Integration/GenericDapServerTests.cs b/test/Dap.Tests/Integration/GenericDapServerTests.cs index 53f8d3471..da531ceff 100644 --- a/test/Dap.Tests/Integration/GenericDapServerTests.cs +++ b/test/Dap.Tests/Integration/GenericDapServerTests.cs @@ -17,7 +17,7 @@ public GenericDapServerTests(ITestOutputHelper outputHelper) : base(new JsonRpcT { } - [RetryFact] + [Fact] public async Task Supports_Multiple_Handlers_On_A_Single_Class() { var handler = new Handler(); diff --git a/test/Dap.Tests/Integration/HandlersManagerIntegrationTests.cs b/test/Dap.Tests/Integration/HandlersManagerIntegrationTests.cs index 017ea2943..035394490 100644 --- a/test/Dap.Tests/Integration/HandlersManagerIntegrationTests.cs +++ b/test/Dap.Tests/Integration/HandlersManagerIntegrationTests.cs @@ -19,7 +19,7 @@ public HandlersManagerIntegrationTests(ITestOutputHelper testOutputHelper) : bas { } - [RetryFact] + [Fact] public async Task Should_Return_Default_Handlers() { var (_, server) = await Initialize(options => {}, options => {}); @@ -29,7 +29,7 @@ public async Task Should_Return_Default_Handlers() handlersManager.GetHandlers().Should().HaveCount(2); } - [RetryFact] + [Fact] public async Task Link_Should_Fail_If_No_Handler_Is_Defined() { var (_, server) = await Initialize(options => {}, options => {}); @@ -40,7 +40,7 @@ public async Task Link_Should_Fail_If_No_Handler_Is_Defined() a.Should().Throw().Which.Message.Should().Contain("Descriptors must be registered before links can be created"); } - [RetryFact] + [Fact] public async Task Link_Should_Fail_If_Link_Is_On_The_Wrong_Side() { var (_, server) = await Initialize(options => {}, options => {}); diff --git a/test/Dap.Tests/Integration/ProgressTests.cs b/test/Dap.Tests/Integration/ProgressTests.cs index 0267bb1f1..1a7b5d885 100644 --- a/test/Dap.Tests/Integration/ProgressTests.cs +++ b/test/Dap.Tests/Integration/ProgressTests.cs @@ -24,7 +24,7 @@ public ProgressTests(ITestOutputHelper outputHelper) : base( { } - [FactWithSkipOn(SkipOnPlatform.All)] + [Fact] public async Task Should_Support_Progress_From_Sever_To_Client() { var (client, server) = await Initialize(ConfigureClient, ConfigureServer); @@ -87,7 +87,7 @@ public async Task Should_Support_Progress_From_Sever_To_Client() results.Should().ContainInOrder("Begin", "Report 1", "Report 2", "Report 3", "Report 4", "End"); } - [FactWithSkipOn(SkipOnPlatform.All)] + [Fact] public async Task Should_Support_Cancelling_Progress_From_Server_To_Client_Request() { var (client, server) = await Initialize(ConfigureClient, ConfigureServer); diff --git a/test/Dap.Tests/Integration/RequestCancellationTests.cs b/test/Dap.Tests/Integration/RequestCancellationTests.cs index 1060a4f35..715f763b7 100644 --- a/test/Dap.Tests/Integration/RequestCancellationTests.cs +++ b/test/Dap.Tests/Integration/RequestCancellationTests.cs @@ -21,7 +21,7 @@ public RequestCancellationTests(ITestOutputHelper outputHelper) : base(new JsonR { } - [RetryFact] + [Fact] public async Task Should_Cancel_Pending_Requests() { var (client, _) = await Initialize(ConfigureClient, ConfigureServer); diff --git a/test/Generation.Tests/AutoImplementParamsGeneratorTests.cs b/test/Generation.Tests/AutoImplementParamsGeneratorTests.cs index a512d39a5..67abc23a2 100644 --- a/test/Generation.Tests/AutoImplementParamsGeneratorTests.cs +++ b/test/Generation.Tests/AutoImplementParamsGeneratorTests.cs @@ -2,12 +2,13 @@ using OmniSharp.Extensions.JsonRpc.Generators; using OmniSharp.Extensions.JsonRpc.Generators.Cache; using TestingUtils; +using Xunit; namespace Generation.Tests { public class AutoImplementParamsGeneratorTests { - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Auto_Magically_Implements_Properties() { var source = @" diff --git a/test/Generation.Tests/EnumLikeStringGeneratorTests.cs b/test/Generation.Tests/EnumLikeStringGeneratorTests.cs index a55a74410..e151a270b 100644 --- a/test/Generation.Tests/EnumLikeStringGeneratorTests.cs +++ b/test/Generation.Tests/EnumLikeStringGeneratorTests.cs @@ -2,12 +2,13 @@ using OmniSharp.Extensions.JsonRpc.Generators; using OmniSharp.Extensions.JsonRpc.Generators.Cache; using TestingUtils; +using Xunit; namespace Generation.Tests { public class EnumLikeStringGeneratorTests { - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Auto_Magically_Implements_IEnumLikeString() { var source = @" diff --git a/test/Generation.Tests/GeneratedRegistrationOptionsTests.cs b/test/Generation.Tests/GeneratedRegistrationOptionsTests.cs index 2c1b00ede..1ff3de714 100644 --- a/test/Generation.Tests/GeneratedRegistrationOptionsTests.cs +++ b/test/Generation.Tests/GeneratedRegistrationOptionsTests.cs @@ -1,12 +1,13 @@ using System.Threading.Tasks; using OmniSharp.Extensions.JsonRpc.Generators; using TestingUtils; +using Xunit; namespace Generation.Tests { public class GeneratedRegistrationOptionsTests { - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Supports_Generating_Strongly_Typed_WorkDone_Registration_Options() { var source = @" @@ -67,7 +68,7 @@ public bool WorkDoneProgress #nullable restore"; await GenerationHelpers.AssertGeneratedAsExpected(source, expected); } - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Supports_Generating_Strongly_Typed_WorkDone_Registration_Options_Interface() { var source = @" @@ -132,7 +133,7 @@ public bool WorkDoneProgress await GenerationHelpers.AssertGeneratedAsExpected(source, expected); } - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Supports_Generating_Strongly_Typed_Registration_Options() { var source = @" @@ -275,7 +276,7 @@ public bool WorkDoneProgress await GenerationHelpers.AssertGeneratedAsExpected(source, expected); } - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Supports_Generating_Strongly_Typed_Registration_Options_With_Converters() { var source = @" diff --git a/test/Generation.Tests/JsonRpcGenerationTests.cs b/test/Generation.Tests/JsonRpcGenerationTests.cs index 42ffab279..87b7e43de 100644 --- a/test/Generation.Tests/JsonRpcGenerationTests.cs +++ b/test/Generation.Tests/JsonRpcGenerationTests.cs @@ -12,7 +12,7 @@ namespace Generation.Tests { public class JsonRpcGenerationTests { - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Supports_Generating_Notifications_And_Infers_Direction_ExitHandler() { var source = @" @@ -74,7 +74,7 @@ public static partial class ExitExtensions await AssertGeneratedAsExpected(source, expected); } - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Supports_Generating_Generic_Response_Types() { var source = @" @@ -182,7 +182,7 @@ public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRe await AssertGeneratedAsExpected(source, expected); } - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Should_Report_Diagnostic_If_Missing_Information() { var source = @" @@ -212,7 +212,7 @@ public interface IExitHandler : IJsonRpcNotificationHandler a.Should().Throw("cache").WithMessage("*Could not infer the request router(s)*"); } - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Supports_Generating_Notifications_And_Infers_Direction_CapabilitiesHandler() { var source = @" @@ -266,7 +266,7 @@ public static partial class CapabilitiesExtensions } - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Supports_Generating_Notifications_ExitHandler() { var source = @" @@ -327,7 +327,7 @@ public static partial class ExitExtensions await AssertGeneratedAsExpected(source, expected); } - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Supports_Generating_Notifications_And_Infers_Direction_DidChangeTextHandler() { var source = @" @@ -417,7 +417,7 @@ public static ILanguageServerRegistry OnDidChangeTextDocument(this ILanguageServ await AssertGeneratedAsExpected(source, expected); } - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Supports_Generating_Notifications_And_Infers_Direction_FoldingRangeHandler() { var source = @" @@ -511,7 +511,7 @@ public static ILanguageServerRegistry ObserveFoldingRange(this ILanguageServerRe await AssertGeneratedAsExpected(source, expected); } - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Supports_Generating_Requests_And_Infers_Direction() { var source = @" @@ -600,7 +600,7 @@ public static ILanguageServerRegistry ObserveDefinition(this ILanguageServerRegi } - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Supports_Generating_Requests() { var source = @" @@ -688,7 +688,7 @@ public static ILanguageServerRegistry ObserveDefinition(this ILanguageServerRegi await AssertGeneratedAsExpected(source, expected); } - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Supports_Custom_Method_Names() { var source = @" @@ -751,7 +751,7 @@ public static partial class LanguageProtocolInitializeExtensions await AssertGeneratedAsExpected(source, expected); } - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Supports_Allow_Derived_Requests() { var source = @" @@ -833,7 +833,7 @@ public static IDebugAdapterServerRegistry OnAttachRequest(this IDebugAdapterS await AssertGeneratedAsExpected(source, expected); } - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Supports_Allow_Derived_Requests_Nullable() { var source = @" @@ -997,7 +997,7 @@ public static IDebugAdapterServerRegistry OnAttachRequest(this IDebugAdapterS await AssertGeneratedAsExpected(source, expected); } - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Supports_Allow_Generic_Types() { var source = @" @@ -1079,7 +1079,7 @@ public static IDebugAdapterServerRegistry OnAttachRequest(this IDebugAdapterS await AssertGeneratedAsExpected(source, expected); } - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Supports_Params_Type_As_Source() { var source = @" diff --git a/test/Generation.Tests/LspFeatureTests.cs b/test/Generation.Tests/LspFeatureTests.cs index c12caa23a..ed42f75f3 100644 --- a/test/Generation.Tests/LspFeatureTests.cs +++ b/test/Generation.Tests/LspFeatureTests.cs @@ -7,7 +7,7 @@ namespace Generation.Tests { public class LspFeatureTests { -// [FactWithSkipOn(SkipOnPlatform.Windows, Skip = "for testing")] +// [Fact(Skip = "for testing"] [Fact] public async Task Supports_Params_Type_As_Source() { @@ -105,7 +105,7 @@ public static ILanguageServerRegistry ObserveWorkspaceSymbols(this ILanguageServ await GenerationHelpers.AssertGeneratedAsExpected(source, expected); } - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Supports_Generating_Custom_Language_Extensions() { var source = @" @@ -373,7 +373,7 @@ public static ILanguageServerRegistry ObserveDiscoverUnitTests(this ILanguageSer await GenerationHelpers.AssertGeneratedAsExpected(source, expectedHandlers); } - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Supports_Generating_Void_Task_Return() { var source = @" diff --git a/test/Generation.Tests/TypedCanBeResolvedTests.cs b/test/Generation.Tests/TypedCanBeResolvedTests.cs index 4e411ea0a..2d7b0b28e 100644 --- a/test/Generation.Tests/TypedCanBeResolvedTests.cs +++ b/test/Generation.Tests/TypedCanBeResolvedTests.cs @@ -1,12 +1,13 @@ using System.Threading.Tasks; using OmniSharp.Extensions.JsonRpc.Generators; using TestingUtils; +using Xunit; namespace Generation.Tests { public class TypedCanBeResolvedTests { - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Supports_Generating_Strongly_Typed_ICanBeResolved_Data() { var source = @" @@ -282,7 +283,7 @@ public CodeLensContainer(params CodeLens[] items): base(items) await GenerationHelpers.AssertGeneratedAsExpected(source, expected); } - [FactWithSkipOn(SkipOnPlatform.Windows)] + [Fact] public async Task Supports_Generating_Strongly_Typed_Container() { var source = @" diff --git a/test/JsonRpc.Tests/IntegrationTests.cs b/test/JsonRpc.Tests/IntegrationTests.cs index 748892b56..755efba60 100644 --- a/test/JsonRpc.Tests/IntegrationTests.cs +++ b/test/JsonRpc.Tests/IntegrationTests.cs @@ -30,7 +30,7 @@ private class Data public string Value { get; set; } = null!; } - [RetryFact] + [Fact] public async Task Should_Send_and_receive_requests() { var (client, server) = await Initialize( @@ -45,7 +45,7 @@ public async Task Should_Send_and_receive_requests() clientResponse.Value.Should().Be("myresponse"); } - [RetryFact] + [Fact] public async Task Should_throw_when_sending_requests() { var (client, server) = await Initialize( @@ -60,7 +60,7 @@ public async Task Should_throw_when_sending_requests() serverRequest.Should().Throw(); } - [RetryFact] + [Fact] public async Task Should_throw_when_receiving_requests() { var (client, server) = await Initialize( @@ -75,7 +75,7 @@ public async Task Should_throw_when_receiving_requests() serverRequest.Should().Throw(); } - [RetryFact] + [Fact] public async Task Should_Send_and_receive_notifications() { var clientNotification = new AsyncSubject(); @@ -108,7 +108,7 @@ public async Task Should_Send_and_receive_notifications() clientResponse.Value.Should().Be("esnopserym"); } - [RetryFact] + [Fact] public async Task Should_Send_and_cancel_requests_immediate() { var (client, server) = await Initialize( @@ -144,7 +144,7 @@ public async Task Should_Send_and_cancel_requests_immediate() } } - [RetryFact] + [Fact] public async Task Should_Send_and_cancel_requests_from_otherside() { var (client, server) = await Initialize( @@ -181,7 +181,7 @@ public async Task Should_Send_and_cancel_requests_from_otherside() } } - [RetryFact] + [Fact] public async Task Should_Cancel_Parallel_Requests_When_Options_Are_Given() { var (client, server) = await Initialize( @@ -232,7 +232,7 @@ public async Task Should_Cancel_Parallel_Requests_When_Options_Are_Given() } } - [RetryFact] + [Fact] public async Task Should_Link_Request_A_to_Request_B() { var (client, server) = await Initialize( diff --git a/test/JsonRpc.Tests/RecursiveResolutionTests.cs b/test/JsonRpc.Tests/RecursiveResolutionTests.cs index f925c0003..16fa140f1 100644 --- a/test/JsonRpc.Tests/RecursiveResolutionTests.cs +++ b/test/JsonRpc.Tests/RecursiveResolutionTests.cs @@ -9,6 +9,7 @@ using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.JsonRpc.Testing; using TestingUtils; +using Xunit; using Xunit.Abstractions; namespace JsonRpc.Tests @@ -19,7 +20,7 @@ public RecursiveResolutionTests(ITestOutputHelper testOutputHelper) : base(new J { } - [FactWithSkipOn(SkipOnPlatform.Windows, Skip = "appears to cause a deadlock")] + [Fact(Skip = "appears to cause a deadlock")] public void Server_Can_Be_Injected_Into_Handler_After_Creation_Using_Registration() { Func a = async () => { @@ -36,7 +37,7 @@ public void Server_Can_Be_Injected_Into_Handler_After_Creation_Using_Registratio a.Should().NotThrow(); } - [FactWithSkipOn(SkipOnPlatform.Windows, Skip = "appears to cause a deadlock")] + [Fact(Skip = "appears to cause a deadlock")] public void Server_Cannot_Be_Injected_Into_Handler_During_Creation_Using_Registration() { Func a = () => Initialize( @@ -49,7 +50,7 @@ public void Server_Cannot_Be_Injected_Into_Handler_During_Creation_Using_Registr result.And.ErrorName.Should().Be("UnableToResolveFromRegisteredServices"); } - [FactWithSkipOn(SkipOnPlatform.Windows, Skip = "appears to cause a deadlock")] + [Fact(Skip = "appears to cause a deadlock")] public void Server_Cannot_Be_Injected_Into_Handler_During_Creation_Using_Description() { Func a = () => Initialize( @@ -62,7 +63,7 @@ public void Server_Cannot_Be_Injected_Into_Handler_During_Creation_Using_Descrip result.And.ErrorName.Should().Be("UnableToResolveFromRegisteredServices"); } - [FactWithSkipOn(SkipOnPlatform.Windows, Skip = "appears to cause a deadlock")] + [Fact(Skip = "appears to cause a deadlock")] public void Server_Cannot_Be_Injected_Into_Handler_During_Creation_Using_Injection() { Func a = () => Initialize( @@ -75,7 +76,7 @@ public void Server_Cannot_Be_Injected_Into_Handler_During_Creation_Using_Injecti result.And.ErrorName.Should().Be("UnableToResolveFromRegisteredServices"); } - [FactWithSkipOn(SkipOnPlatform.Windows, Skip = "appears to cause a deadlock")] + [Fact(Skip = "appears to cause a deadlock")] public async Task Server_Facade_Can_Be_Injected_Into_Handler_During_Creation_Using_Registration() { Func a = () => Initialize( @@ -86,7 +87,7 @@ public async Task Server_Facade_Can_Be_Injected_Into_Handler_During_Creation_Usi await a.Should().NotThrowAsync(); } - [FactWithSkipOn(SkipOnPlatform.Windows, Skip = "appears to cause a deadlock")] + [Fact(Skip = "appears to cause a deadlock")] public async Task Server_Facade_Can_Be_Injected_Into_Handler_During_Creation_Using_Description() { Func a = () => Initialize( @@ -97,7 +98,7 @@ public async Task Server_Facade_Can_Be_Injected_Into_Handler_During_Creation_Usi await a.Should().NotThrowAsync(); } - [FactWithSkipOn(SkipOnPlatform.Windows, Skip = "appears to cause a deadlock")] + [Fact(Skip = "appears to cause a deadlock")] public async Task Server_Facade_Can_Injected_Into_Handler_During_Creation_Using_Injection() { Func a = () => Initialize( diff --git a/test/Lsp.Tests/Integration/ConnectionAndDisconnectionTests.cs b/test/Lsp.Tests/Integration/ConnectionAndDisconnectionTests.cs index f67baa0f3..7ccfd30b7 100644 --- a/test/Lsp.Tests/Integration/ConnectionAndDisconnectionTests.cs +++ b/test/Lsp.Tests/Integration/ConnectionAndDisconnectionTests.cs @@ -22,7 +22,7 @@ public ConnectionAndDisconnectionTests(ITestOutputHelper outputHelper) : base( { } - [RetryFact] + [Fact] public async Task Server_Should_Stay_Alive_When_Requests_Throw_An_Exception() { var (client, _) = await Initialize(ConfigureClient, ConfigureServer); @@ -37,7 +37,7 @@ public async Task Server_Should_Stay_Alive_When_Requests_Throw_An_Exception() result.Should().BeTrue(); } - [RetryFact] + [Fact] public async Task Client_Should_Stay_Alive_When_Requests_Throw_An_Exception() { var (_, server) = await Initialize(ConfigureClient, ConfigureServer); @@ -52,7 +52,7 @@ public async Task Client_Should_Stay_Alive_When_Requests_Throw_An_Exception() result.Should().BeTrue(); } - [RetryFact] + [Fact] public async Task Server_Should_Support_Links() { var (client, _) = await Initialize(ConfigureClient, ConfigureServer); @@ -67,7 +67,7 @@ public async Task Server_Should_Support_Links() result.Should().BeTrue(); } - [RetryFact] + [Fact] public async Task Client_Should_Support_Links() { var (_, server) = await Initialize(ConfigureClient, ConfigureServer); diff --git a/test/Lsp.Tests/Integration/CustomRequestsTests.cs b/test/Lsp.Tests/Integration/CustomRequestsTests.cs index a49ae20f4..042480be5 100644 --- a/test/Lsp.Tests/Integration/CustomRequestsTests.cs +++ b/test/Lsp.Tests/Integration/CustomRequestsTests.cs @@ -10,6 +10,7 @@ using OmniSharp.Extensions.LanguageServer.Protocol.Models; using OmniSharp.Extensions.LanguageServer.Protocol.Window; using TestingUtils; +using Xunit; using Xunit.Abstractions; namespace Lsp.Tests.Integration @@ -20,7 +21,7 @@ public CustomRequestsTests(ITestOutputHelper outputHelper) : base(new JsonRpcTes { } - [RetryFact] + [Fact] public async Task Should_Support_Custom_Telemetry_Using_Base_Class() { var fake = Substitute.For>(); @@ -34,17 +35,17 @@ public async Task Should_Support_Custom_Telemetry_Using_Base_Class() PromptToUpdatePackageManagement = false }; server.SendTelemetryEvent(@event); - await SettleNext(); + await TestHelper.DelayUntil(() => fake.ReceivedCalls().Any(), CancellationToken); var call = fake.ReceivedCalls().Single(); var args = call.GetArguments(); args[0].Should().BeOfType() .And.Subject - .Should().BeEquivalentTo(@event, z=> z.UsingStructuralRecordEquality().Excluding(x => x.ExtensionData)); + .Should().BeEquivalentTo(@event, z => z.UsingStructuralRecordEquality().Excluding(x => x.ExtensionData)); } - [RetryFact] + [Fact] public async Task Should_Support_Custom_Telemetry_Receiving_Regular_Telemetry_Using_Base_Class() { var fake = Substitute.For(); @@ -58,7 +59,7 @@ public async Task Should_Support_Custom_Telemetry_Receiving_Regular_Telemetry_Us PromptToUpdatePackageManagement = false }; server.SendTelemetryEvent(@event); - await SettleNext(); + await TestHelper.DelayUntil(() => fake.ReceivedCalls().Any(), CancellationToken); var call = fake.ReceivedCalls().Single(); var args = call.GetArguments(); @@ -70,11 +71,11 @@ public async Task Should_Support_Custom_Telemetry_Receiving_Regular_Telemetry_Us request.ExtensionData.Should().ContainKey("promptToUpdatePackageManagement").And.Subject["promptToUpdatePackageManagement"].Should().Be(false); } - [RetryFact] + [Fact] public async Task Should_Support_Custom_Telemetry_Using_Extension_Data_Using_Base_Class() { var fake = Substitute.For>(); - var (_, server) = await Initialize(options => { options.AddHandler(fake);}, options => { }); + var (_, server) = await Initialize(options => { options.AddHandler(fake); }, options => { }); server.SendTelemetryEvent( new TelemetryEventParams { @@ -87,7 +88,7 @@ public async Task Should_Support_Custom_Telemetry_Using_Extension_Data_Using_Bas } } ); - await SettleNext(); + await TestHelper.DelayUntil(() => fake.ReceivedCalls().Any(), CancellationToken); var call = fake.ReceivedCalls().Single(); var args = call.GetArguments(); @@ -99,7 +100,7 @@ public async Task Should_Support_Custom_Telemetry_Using_Extension_Data_Using_Bas request.PromptToUpdatePackageManagement.Should().Be(false); } - [RetryFact] + [Fact] public async Task Should_Support_Custom_Telemetry_Using_Delegate() { var fake = Substitute.For>(); @@ -113,17 +114,17 @@ public async Task Should_Support_Custom_Telemetry_Using_Delegate() PromptToUpdatePackageManagement = false }; server.SendTelemetryEvent(@event); - await SettleNext(); + await TestHelper.DelayUntil(() => fake.ReceivedCalls().Any(), CancellationToken); var call = fake.ReceivedCalls().Single(); var args = call.GetArguments(); args[0] .Should().BeOfType() .And.Subject - .Should().BeEquivalentTo(@event, z=> z.UsingStructuralRecordEquality().Excluding(x => x.ExtensionData)); + .Should().BeEquivalentTo(@event, z => z.UsingStructuralRecordEquality().Excluding(x => x.ExtensionData)); } - [RetryFact] + [Fact] public async Task Should_Support_Custom_Telemetry_Receiving_Regular_Telemetry_Using_Delegate() { var fake = Substitute.For>(); @@ -137,7 +138,7 @@ public async Task Should_Support_Custom_Telemetry_Receiving_Regular_Telemetry_Us PromptToUpdatePackageManagement = false }; server.SendTelemetryEvent(@event); - await SettleNext(); + await TestHelper.DelayUntil(() => fake.ReceivedCalls().Any(), CancellationToken); var call = fake.ReceivedCalls().Single(); var args = call.GetArguments(); @@ -149,7 +150,7 @@ public async Task Should_Support_Custom_Telemetry_Receiving_Regular_Telemetry_Us request.ExtensionData.Should().ContainKey("promptToUpdatePackageManagement").And.Subject["promptToUpdatePackageManagement"].Should().Be(false); } - [RetryFact] + [Fact] public async Task Should_Support_Custom_Telemetry_Using_Extension_Data_Using_Delegate() { var fake = Substitute.For>(); @@ -166,7 +167,7 @@ public async Task Should_Support_Custom_Telemetry_Using_Extension_Data_Using_Del } } ); - await SettleNext(); + await TestHelper.DelayUntil(() => fake.ReceivedCalls().Any(), CancellationToken); var call = fake.ReceivedCalls().Single(); var args = call.GetArguments(); diff --git a/test/Lsp.Tests/Integration/DisableDefaultsTests.cs b/test/Lsp.Tests/Integration/DisableDefaultsTests.cs index 3ed3fdd08..87a0998f1 100644 --- a/test/Lsp.Tests/Integration/DisableDefaultsTests.cs +++ b/test/Lsp.Tests/Integration/DisableDefaultsTests.cs @@ -29,7 +29,7 @@ public DisableDefaultsTests(ITestOutputHelper outputHelper) : base(new JsonRpcTe { } - [RetryFact] + [Fact] public async Task Should_Disable_Registration_Manager() { var registrationAction = Substitute.For>(); @@ -48,7 +48,7 @@ public async Task Should_Disable_Registration_Manager() clientManager.Descriptors.Should().ContainSingle(f => f.Method == ClientNames.UnregisterCapability); } - [RetryFact] + [Fact] public async Task Should_Disable_Workspace_Folder_Manager() { var clientAction = Substitute.For?>>>(); @@ -67,7 +67,7 @@ public async Task Should_Disable_Workspace_Folder_Manager() serverManager.Descriptors.Should().ContainSingle(f => f.Method == WorkspaceNames.DidChangeWorkspaceFolders); } - [RetryFact] + [Fact] public async Task Should_Allow_Custom_Workspace_Folder_Manager_Delegate() { var action = Substitute.For>(); @@ -97,7 +97,7 @@ await TestHelper.DelayUntil( ); } - [RetryFact] + [Fact] public async Task Should_Disable_Configuration() { var action = Substitute.For>(); @@ -111,7 +111,7 @@ public async Task Should_Disable_Configuration() serverManager.Descriptors.Should().ContainSingle(f => f.Method == WorkspaceNames.DidChangeConfiguration); } - [RetryFact] + [Fact] public async Task Should_Allow_Custom_Configuration_Delegate() { var action = Substitute.For>(); diff --git a/test/Lsp.Tests/Integration/DynamicRegistrationTests.cs b/test/Lsp.Tests/Integration/DynamicRegistrationTests.cs index 60df90996..d5059a3cb 100644 --- a/test/Lsp.Tests/Integration/DynamicRegistrationTests.cs +++ b/test/Lsp.Tests/Integration/DynamicRegistrationTests.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Reactive.Disposables; using System.Reactive.Linq; using System.Threading.Tasks; using FluentAssertions; @@ -27,7 +28,7 @@ public static class DynamicRegistration { public class DynamicRegistrationTests : LanguageProtocolTestBase { - [RetryFact] + [Fact] public async Task Should_Register_Dynamically_After_Initialization() { var (client, _) = await Initialize(new ConfigureClient().Configure, new ConfigureServer().Configure); @@ -49,7 +50,7 @@ await TestHelper.DelayUntil( ); } - [RetryFact] + [Fact] public async Task Should_Register_Dynamically_While_Server_Is_Running() { var (client, server) = await Initialize(new ConfigureClient().Configure, new ConfigureServer().Configure); @@ -98,7 +99,7 @@ await TestHelper.DelayUntil( client.RegistrationManager.CurrentRegistrations.Should().Contain(x => x.Method == "@/" + TextDocumentNames.Completion); } - [RetryFact] + [Fact] public async Task Should_Unregister_Dynamically_While_Server_Is_Running() { var (client, server) = await Initialize(new ConfigureClient().Configure, new ConfigureServer().Configure); @@ -114,19 +115,24 @@ public async Task Should_Unregister_Dynamically_While_Server_Is_Running() } ) ); + + // Sometimes we come through and this fails + disposable.Should().BeOfType().Subject.Count.Should().Be(2); + await TestHelper.DelayUntil( () => client.RegistrationManager.CurrentRegistrations, registrations => registrations.Any(registration => SelectorMatches(registration, x => x.HasLanguage && x.Language == "vb")), CancellationToken ); - disposable.Dispose(); + disposable.Dispose(); await TestHelper.DelayUntil( () => client.RegistrationManager.CurrentRegistrations, registrations => !registrations.Any(registration => SelectorMatches(registration, x => x.HasLanguage && x.Language == "vb")), CancellationToken ); + await Task.Delay(200); client.RegistrationManager.CurrentRegistrations.Should().NotContain( x => @@ -134,7 +140,7 @@ await TestHelper.DelayUntil( ); } - [RetryFact] + [Fact] public async Task Should_Only_Register_Semantic_Tokens_Registration_Once() { var tokens = Substitute.For(); @@ -182,7 +188,7 @@ public StaticDynamicRegistrationTests(ITestOutputHelper testOutputHelper) : base { } - [RetryFact] + [Fact] public async Task Should_Gather_Static_Registrations() { var (client, _) = await Initialize( @@ -217,7 +223,7 @@ await TestHelper.DelayUntil( client.RegistrationManager.CurrentRegistrations.Should().Contain(x => x.Method == TextDocumentNames.SemanticTokensRegistration); } - [RetryFact] + [Fact] public async Task Should_Register_Static_When_Dynamic_Is_Disabled() { var (client, server) = await Initialize( diff --git a/test/Lsp.Tests/Integration/ErroringHandlingTests.cs b/test/Lsp.Tests/Integration/ErroringHandlingTests.cs index 6d7f7ff70..581d12a5b 100644 --- a/test/Lsp.Tests/Integration/ErroringHandlingTests.cs +++ b/test/Lsp.Tests/Integration/ErroringHandlingTests.cs @@ -23,7 +23,7 @@ public ErroringHandlingTests(ITestOutputHelper outputHelper) : base(new JsonRpcT { } - [RetryFact] + [Fact] public async Task Should_Handle_Malformed_Request() { var (client, server) = await Initialize(ConfigureClient, ConfigureServer); @@ -46,7 +46,7 @@ public async Task Should_Handle_Malformed_Request() a.Should().Throw(); } - [RetryFact] + [Fact] public async Task Should_Handle_Malformed_Notification() { var (client, server) = await Initialize(ConfigureClient, ConfigureServer); diff --git a/test/Lsp.Tests/Integration/EventingTests.cs b/test/Lsp.Tests/Integration/EventingTests.cs index 472ee7e37..1a9cc5164 100644 --- a/test/Lsp.Tests/Integration/EventingTests.cs +++ b/test/Lsp.Tests/Integration/EventingTests.cs @@ -27,7 +27,7 @@ public EventingTests(ITestOutputHelper outputHelper) : base(new JsonRpcTestOptio { } - [RetryFact] + [Fact] public async Task Initialize_Interface_Is_Supported() { var onLanguageClientInitialize = Substitute.For(); @@ -41,7 +41,7 @@ public async Task Initialize_Interface_Is_Supported() await onLanguageServerInitialize.Received(1).OnInitialize(server, server.ClientSettings, Arg.Any()); } - [RetryFact] + [Fact] public async Task Initialize_Delegate_Is_Supported() { var onLanguageClientInitialize = Substitute.For(); @@ -55,7 +55,7 @@ public async Task Initialize_Delegate_Is_Supported() await onLanguageServerInitialize.Received(1).Invoke(server, server.ClientSettings, Arg.Any()); } - [RetryFact] + [Fact] public async Task Initialize_Interface_Is_Supported_On_Handlers() { var onLanguageClientInitialize = @@ -71,7 +71,7 @@ public async Task Initialize_Interface_Is_Supported_On_Handlers() await onLanguageServerInitialize.Received(1).OnInitialize(server, server.ClientSettings, Arg.Any()); } - [RetryFact] + [Fact] public async Task Initialize_Interface_Is_Supported_On_Handlers_After_Startup() { var onLanguageClientInitialize = @@ -90,7 +90,7 @@ public async Task Initialize_Interface_Is_Supported_On_Handlers_After_Startup() await onLanguageServerInitialize.Received(1).OnInitialize(server, server.ClientSettings, Arg.Any()); } - [RetryFact] + [Fact] public async Task Initialized_Interface_Is_Supported() { var onLanguageClientInitialized = Substitute.For(); @@ -104,7 +104,7 @@ public async Task Initialized_Interface_Is_Supported() await onLanguageServerInitialized.Received(1).OnInitialized(server, server.ClientSettings, server.ServerSettings, Arg.Any()); } - [RetryFact] + [Fact] public async Task Initialized_Delegate_Is_Supported() { var onLanguageClientInitialized = Substitute.For(); @@ -118,7 +118,7 @@ public async Task Initialized_Delegate_Is_Supported() await onLanguageServerInitialized.Received(1).Invoke(server, server.ClientSettings, server.ServerSettings, Arg.Any()); } - [RetryFact] + [Fact] public async Task Initialized_Interface_Is_Supported_On_Handlers() { var onLanguageClientInitialized = @@ -134,7 +134,7 @@ public async Task Initialized_Interface_Is_Supported_On_Handlers() await onLanguageServerInitialized.Received(1).OnInitialized(server, server.ClientSettings, server.ServerSettings, Arg.Any()); } - [RetryFact] + [Fact] public async Task Initialized_Interface_Is_Supported_On_Handlers_After_Startup() { var onLanguageClientInitialized = @@ -153,7 +153,7 @@ public async Task Initialized_Interface_Is_Supported_On_Handlers_After_Startup() await onLanguageServerInitialized.Received(1).OnInitialized(server, server.ClientSettings, server.ServerSettings, Arg.Any()); } - [RetryFact] + [Fact] public async Task Started_Interface_Is_Supported() { var onLanguageClientStarted = Substitute.For(); @@ -167,7 +167,7 @@ public async Task Started_Interface_Is_Supported() await onLanguageServerStarted.Received(1).OnStarted(server, Arg.Any()); } - [RetryFact] + [Fact] public async Task Started_Delegate_Is_Supported() { var onLanguageClientStarted = Substitute.For(); @@ -181,7 +181,7 @@ public async Task Started_Delegate_Is_Supported() await onLanguageServerStarted.Received(1).Invoke(server, Arg.Any()); } - [RetryFact] + [Fact] public async Task Started_Interface_Is_Supported_On_Handlers() { var onLanguageClientStarted = @@ -197,7 +197,7 @@ public async Task Started_Interface_Is_Supported_On_Handlers() await onLanguageServerStarted.Received(1).OnStarted(server, Arg.Any()); } - [RetryFact] + [Fact] public async Task Started_Interface_Is_Supported_On_Handlers_After_Startup() { var onLanguageClientStarted = diff --git a/test/Lsp.Tests/Integration/ExecuteCommandTests.cs b/test/Lsp.Tests/Integration/ExecuteCommandTests.cs index fa8554321..d059aa2b9 100644 --- a/test/Lsp.Tests/Integration/ExecuteCommandTests.cs +++ b/test/Lsp.Tests/Integration/ExecuteCommandTests.cs @@ -28,7 +28,7 @@ public ExecuteCommandTests(ITestOutputHelper outputHelper) : base(new JsonRpcTes { } - [RetryFact] + [Fact] public async Task Should_Execute_A_Command() { var command = Substitute.For>(); @@ -65,7 +65,7 @@ public async Task Should_Execute_A_Command() await command.Received(1).Invoke(Arg.Any()); } - [RetryFact] + [Fact] public async Task Should_Execute_The_Correct_Command() { var commanda = Substitute.For>(); @@ -118,7 +118,7 @@ public async Task Should_Execute_The_Correct_Command() arg.Should().BeOfType(); } - [RetryFact] + [Fact] public async Task Should_Fail_To_Execute_A_Command_When_No_Command_Is_Defined() { var (client, _) = await Initialize( @@ -147,7 +147,7 @@ public async Task Should_Fail_To_Execute_A_Command_When_No_Command_Is_Defined() await action.Should().ThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Fail_To_Execute_A_Command_When_No_Command_Name_Is_Given() { var command = Substitute.For>(); @@ -187,7 +187,7 @@ public async Task Should_Fail_To_Execute_A_Command_When_No_Command_Name_Is_Given await command.Received(0).Invoke(Arg.Any()); } - [RetryFact] + [Fact] public async Task Should_Fail_To_Execute_A_Command() { var commandc = Substitute.For>(); @@ -233,7 +233,7 @@ public async Task Should_Fail_To_Execute_A_Command() await commandb.Received(0).Invoke(Arg.Any()); } - [RetryFact] + [Fact] public async Task Should_Execute_1_Args() { var (client, _) = await Initialize( @@ -270,7 +270,7 @@ public async Task Should_Execute_1_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_2_Args() { var (client, _) = await Initialize( @@ -308,7 +308,7 @@ public async Task Should_Execute_2_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_3_Args() { var (client, _) = await Initialize( @@ -347,7 +347,7 @@ public async Task Should_Execute_3_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_4_Args() { var (client, _) = await Initialize( @@ -387,7 +387,7 @@ public async Task Should_Execute_4_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_5_Args() { var (client, _) = await Initialize( @@ -430,7 +430,7 @@ public async Task Should_Execute_5_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_6_Args() { var (client, _) = await Initialize( @@ -475,7 +475,7 @@ public async Task Should_Execute_6_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_1_With_Missing_Args() { var (client, _) = await Initialize( @@ -512,7 +512,7 @@ public async Task Should_Execute_1_With_Missing_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_2_With_Missing_Args() { var (client, _) = await Initialize( @@ -550,7 +550,7 @@ public async Task Should_Execute_2_With_Missing_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_3_With_Missing_Args() { var (client, _) = await Initialize( @@ -589,7 +589,7 @@ public async Task Should_Execute_3_With_Missing_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_4_With_Missing_Args() { var (client, _) = await Initialize( @@ -629,7 +629,7 @@ public async Task Should_Execute_4_With_Missing_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_5_With_Missing_Args() { var (client, _) = await Initialize( @@ -670,7 +670,7 @@ public async Task Should_Execute_5_With_Missing_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_6_With_Missing_Args() { var (client, _) = await Initialize( @@ -712,7 +712,7 @@ public async Task Should_Execute_6_With_Missing_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_1_Null_Args() { var (client, _) = await Initialize( @@ -749,7 +749,7 @@ public async Task Should_Execute_1_Null_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_2_Null_Args() { var (client, _) = await Initialize( @@ -787,7 +787,7 @@ public async Task Should_Execute_2_Null_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_3_Null_Args() { var (client, _) = await Initialize( @@ -826,7 +826,7 @@ public async Task Should_Execute_3_Null_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_4_Null_Args() { var (client, _) = await Initialize( @@ -866,7 +866,7 @@ public async Task Should_Execute_4_Null_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_5_Null_Args() { var (client, _) = await Initialize( @@ -907,7 +907,7 @@ public async Task Should_Execute_5_Null_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_6_Null_Args() { var (client, _) = await Initialize( diff --git a/test/Lsp.Tests/Integration/ExecuteTypedCommandTests.cs b/test/Lsp.Tests/Integration/ExecuteTypedCommandTests.cs index 0937a389d..4caf445ae 100644 --- a/test/Lsp.Tests/Integration/ExecuteTypedCommandTests.cs +++ b/test/Lsp.Tests/Integration/ExecuteTypedCommandTests.cs @@ -28,7 +28,7 @@ public ExecuteTypedCommandTests(ITestOutputHelper outputHelper) : base(new JsonR { } - [RetryFact] + [Fact] public async Task Should_Execute_A_Command() { var command = Substitute.For, Task>>(); @@ -66,7 +66,7 @@ public async Task Should_Execute_A_Command() await command.Received(1).Invoke(Arg.Any>()); } - [RetryFact] + [Fact] public async Task Should_Execute_The_Correct_Command() { var commanda = Substitute.For, Task>>(); @@ -122,7 +122,7 @@ public async Task Should_Execute_The_Correct_Command() arg.Should().BeOfType(); } - [RetryFact] + [Fact] public async Task Should_Fail_To_Execute_A_Command_When_No_Command_Name_Is_Given() { var command = Substitute.For,Task>>(); @@ -163,7 +163,7 @@ public async Task Should_Fail_To_Execute_A_Command_When_No_Command_Name_Is_Given await command.Received(0).Invoke(Arg.Any>()); } - [RetryFact] + [Fact] public async Task Should_Fail_To_Execute_A_Command() { var commandc = Substitute.For,Task>>(); @@ -211,7 +211,7 @@ public async Task Should_Fail_To_Execute_A_Command() await commandb.Received(0).Invoke(Arg.Any>()); } - [RetryFact] + [Fact] public async Task Should_Execute_1_Args() { var (client, _) = await Initialize( @@ -248,7 +248,7 @@ public async Task Should_Execute_1_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_2_Args() { var (client, _) = await Initialize( @@ -286,7 +286,7 @@ public async Task Should_Execute_2_Args() response.Value.Should().Be("1234"); } - [RetryFact] + [Fact] public async Task Should_Execute_3_Args() { var (client, _) = await Initialize( @@ -325,7 +325,7 @@ public async Task Should_Execute_3_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_4_Args() { var (client, _) = await Initialize( @@ -365,7 +365,7 @@ public async Task Should_Execute_4_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_5_Args() { var (client, _) = await Initialize( @@ -408,7 +408,7 @@ public async Task Should_Execute_5_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_6_Args() { var (client, _) = await Initialize( @@ -453,7 +453,7 @@ public async Task Should_Execute_6_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_1_With_Missing_Args() { var (client, _) = await Initialize( @@ -490,7 +490,7 @@ public async Task Should_Execute_1_With_Missing_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_2_With_Missing_Args() { var (client, _) = await Initialize( @@ -528,7 +528,7 @@ public async Task Should_Execute_2_With_Missing_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_3_With_Missing_Args() { var (client, _) = await Initialize( @@ -567,7 +567,7 @@ public async Task Should_Execute_3_With_Missing_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_4_With_Missing_Args() { var (client, _) = await Initialize( @@ -607,7 +607,7 @@ public async Task Should_Execute_4_With_Missing_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_5_With_Missing_Args() { var (client, _) = await Initialize( @@ -648,7 +648,7 @@ public async Task Should_Execute_5_With_Missing_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_6_With_Missing_Args() { var (client, _) = await Initialize( @@ -690,7 +690,7 @@ public async Task Should_Execute_6_With_Missing_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_1_Null_Args() { var (client, _) = await Initialize( @@ -727,7 +727,7 @@ public async Task Should_Execute_1_Null_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_2_Null_Args() { var (client, _) = await Initialize( @@ -765,7 +765,7 @@ public async Task Should_Execute_2_Null_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_3_Null_Args() { var (client, _) = await Initialize( @@ -804,7 +804,7 @@ public async Task Should_Execute_3_Null_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_4_Null_Args() { var (client, _) = await Initialize( @@ -844,7 +844,7 @@ public async Task Should_Execute_4_Null_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_5_Null_Args() { var (client, _) = await Initialize( @@ -885,7 +885,7 @@ public async Task Should_Execute_5_Null_Args() await action.Should().NotThrowAsync(); } - [RetryFact] + [Fact] public async Task Should_Execute_6_Null_Args() { var (client, _) = await Initialize( diff --git a/test/Lsp.Tests/Integration/ExtensionTests.cs b/test/Lsp.Tests/Integration/ExtensionTests.cs index 72a77c23b..1405c7c81 100644 --- a/test/Lsp.Tests/Integration/ExtensionTests.cs +++ b/test/Lsp.Tests/Integration/ExtensionTests.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Reactive.Linq; using System.Reactive.Threading.Tasks; using System.Threading; @@ -28,7 +29,7 @@ public ExtensionTests(ITestOutputHelper outputHelper) : base(new JsonRpcTestOpti { } - [RetryFact] + [Fact] public async Task Should_Support_Custom_Capabilities() { var onDiscoverHandler = Substitute.For>>>(); @@ -80,7 +81,7 @@ public async Task Should_Support_Custom_Capabilities() } { - await client.RegistrationManager.Registrations.Throttle(TimeSpan.FromMilliseconds(300)).Take(1).ToTask(CancellationToken); + await TestHelper.DelayUntil(() => client.RegistrationManager.CurrentRegistrations.Any(z => z.Method == "tests"), CancellationToken); client.RegistrationManager.CurrentRegistrations.Should().Contain(z => z.Method == "tests").And.HaveCount(1); } @@ -96,7 +97,7 @@ await client.RequestDiscoverUnitTests( onRunUnitHandler.Received(1).Invoke(Arg.Any(), Arg.Is(x => x.Property == "Abcd"), Arg.Any()); } - [RetryFact] + [Fact] public async Task Should_Support_Custom_Capabilities_Using_Json() { var onDiscoverHandler = Substitute.For>>>(); @@ -130,7 +131,7 @@ public async Task Should_Support_Custom_Capabilities_Using_Json() } { - await client.RegistrationManager.Registrations.Throttle(TimeSpan.FromMilliseconds(300)).Take(1).ToTask(CancellationToken); + await TestHelper.DelayUntil(() => client.RegistrationManager.CurrentRegistrations.Any(z => z.Method == "tests"), CancellationToken); client.RegistrationManager.CurrentRegistrations.Should().Contain(z => z.Method == "tests").And.HaveCount(1); } @@ -141,7 +142,7 @@ public async Task Should_Support_Custom_Capabilities_Using_Json() onRunUnitHandler.Received(1).Invoke(Arg.Any(), Arg.Is(x => x.Property == "Abcd"), Arg.Any()); } - [RetryFact] + [Fact] public async Task Should_Support_Custom_Static_Options() { var onDiscoverHandler = Substitute.For>>>(); @@ -179,7 +180,7 @@ public async Task Should_Support_Custom_Static_Options() } } - [RetryFact] + [Fact] public async Task Should_Convert_Registration_Options_Into_Static_Options_As_Required() { var (client, _) = await Initialize( diff --git a/test/Lsp.Tests/Integration/FileOperationTests.cs b/test/Lsp.Tests/Integration/FileOperationTests.cs index eb36d280b..6fc4fb275 100644 --- a/test/Lsp.Tests/Integration/FileOperationTests.cs +++ b/test/Lsp.Tests/Integration/FileOperationTests.cs @@ -12,6 +12,7 @@ using OmniSharp.Extensions.LanguageServer.Server; using Serilog.Events; using TestingUtils; +using Xunit; using Xunit.Abstractions; namespace Lsp.Tests.Integration @@ -29,7 +30,7 @@ public FileOperationTests(ITestOutputHelper outputHelper) : base(new JsonRpcTest { } - [RetryFact] + [Fact] public async Task Should_Handle_FileCreate() { var (client, server) = await Initialize(Configure, Configure); @@ -45,7 +46,7 @@ await client.RequestWillCreateFile( } ); - await SettleNext(); + await TestHelper.DelayUntil(() => _didCreateFileHandler.ReceivedCalls().Any(), CancellationToken); _didCreateFileHandler.ReceivedCalls().Should().HaveCount(1); _willCreateFileHandler.ReceivedCalls().Should().HaveCount(1); @@ -75,7 +76,7 @@ static void VerifyClientSettings(InitializeParams result) } } - [RetryFact] + [Fact] public async Task Should_Handle_FileRename() { var (client, server) = await Initialize(Configure, Configure); @@ -91,7 +92,7 @@ await client.RequestWillRenameFile( } ); - await SettleNext(); + await TestHelper.DelayUntil(() => _didRenameFileHandler.ReceivedCalls().Any(), CancellationToken); _didRenameFileHandler.ReceivedCalls().Should().HaveCount(1); _willRenameFileHandler.ReceivedCalls().Should().HaveCount(1); @@ -121,7 +122,7 @@ static void VerifyClientSettings(InitializeParams result) } } - [RetryFact] + [Fact] public async Task Should_Handle_FileDelete() { var (client, server) = await Initialize(Configure, Configure); @@ -137,7 +138,7 @@ await client.RequestWillDeleteFile( } ); - await SettleNext(); + await TestHelper.DelayUntil(() => _didDeleteFileHandler.ReceivedCalls().Any(), CancellationToken); _didDeleteFileHandler.ReceivedCalls().Should().HaveCount(1); _willDeleteFileHandler.ReceivedCalls().Should().HaveCount(1); diff --git a/test/Lsp.Tests/Integration/Fixtures/LanguageProtocolFixture.cs b/test/Lsp.Tests/Integration/Fixtures/LanguageProtocolFixture.cs index 4b7c7a84b..fd9543ab0 100644 --- a/test/Lsp.Tests/Integration/Fixtures/LanguageProtocolFixture.cs +++ b/test/Lsp.Tests/Integration/Fixtures/LanguageProtocolFixture.cs @@ -14,20 +14,30 @@ public class LanguageProtocolFixture {}, options => {}); @@ -30,7 +30,7 @@ public async Task Should_Return_Default_Handlers() handlersManager.GetHandlers().Should().HaveCount(6); } - [RetryFact] + [Fact] public async Task Should_Return_Additional_Handlers() { var (_, server) = await Initialize(options => {}, options => {}); @@ -41,7 +41,7 @@ public async Task Should_Return_Additional_Handlers() handlersManager.GetHandlers().Should().HaveCount(7); } - [RetryFact] + [Fact] public async Task Link_Should_Fail_If_No_Handler_Is_Defined() { var (_, server) = await Initialize(options => {}, options => {}); @@ -52,7 +52,7 @@ public async Task Link_Should_Fail_If_No_Handler_Is_Defined() a.Should().Throw().Which.Message.Should().Contain("Descriptors must be registered before links can be created"); } - [RetryFact] + [Fact] public async Task Link_Should_Fail_If_Link_Is_On_The_Wrong_Side() { var (_, server) = await Initialize(options => {}, options => {}); diff --git a/test/Lsp.Tests/Integration/InitializationTests.cs b/test/Lsp.Tests/Integration/InitializationTests.cs index ade4633b8..6c783cf6c 100644 --- a/test/Lsp.Tests/Integration/InitializationTests.cs +++ b/test/Lsp.Tests/Integration/InitializationTests.cs @@ -33,7 +33,7 @@ public InitializationTests(ITestOutputHelper outputHelper) : base(new JsonRpcTes { } - [RetryFact] + [Fact] public async Task Logs_should_be_allowed_during_startup() { await Initialize(ConfigureClient, ConfigureServer); @@ -42,7 +42,7 @@ public async Task Logs_should_be_allowed_during_startup() _logs.Should().ContainInOrder("OnInitialize", "OnInitialized"); } - [RetryFact] + [Fact] public async Task Facades_should_be_resolvable() { var (client, server) = await Initialize(ConfigureClient, ConfigureServer); @@ -54,7 +54,7 @@ public async Task Facades_should_be_resolvable() response.Should().NotBeNull(); } - [RetryFact] + [Fact] public async Task Should_Not_Be_Able_To_Send_Messages_Unit_Initialization() { if (!(TestOptions.ClientLoggerFactory is TestLoggerFactory loggerFactory)) throw new Exception("wtf"); @@ -99,7 +99,7 @@ public async Task Should_Not_Be_Able_To_Send_Messages_Unit_Initialization() onInitializedNotify.Received(1).Invoke(); } - [RetryFact] + [Fact] public async Task Should_Be_Able_To_Register_Before_Initialize() { var (client, server) = Create(options => options.EnableDynamicRegistration().EnableAllCapabilities(), options => { }); diff --git a/test/Lsp.Tests/Integration/LanguageServerConfigurationTests.cs b/test/Lsp.Tests/Integration/LanguageServerConfigurationTests.cs index 0e85ed2ad..af31698b0 100644 --- a/test/Lsp.Tests/Integration/LanguageServerConfigurationTests.cs +++ b/test/Lsp.Tests/Integration/LanguageServerConfigurationTests.cs @@ -9,6 +9,7 @@ using Newtonsoft.Json.Linq; using NSubstitute; using NSubstitute.Extensions; +using NSubstitute.ReceivedExtensions; using OmniSharp.Extensions.JsonRpc.Testing; using OmniSharp.Extensions.LanguageProtocol.Testing; using OmniSharp.Extensions.LanguageServer.Client; @@ -253,7 +254,7 @@ public async Task Should_Support_Options_Monitor() // IOptionsMonitor<> is registered as a singleton, so this will update options.CurrentValue.Host.Should().Be("localhost"); options.CurrentValue.Port.Should().Be(443); - sub.Received(1).Invoke(Arg.Any()); + sub.Received(Quantity.AtLeastOne()).Invoke(Arg.Any()); configuration.Update("mysection", new Dictionary { ["host"] = "127.0.0.1", ["port"] = "80" }); await options.WaitForChange(CancellationToken); @@ -261,7 +262,7 @@ public async Task Should_Support_Options_Monitor() options.CurrentValue.Host.Should().Be("127.0.0.1"); options.CurrentValue.Port.Should().Be(80); - sub.Received(2).Invoke(Arg.Any()); + sub.Received(Quantity.Within(2, int.MaxValue)).Invoke(Arg.Any()); } class BinderSourceUrl diff --git a/test/Lsp.Tests/Integration/LanguageServerLoggingTests.cs b/test/Lsp.Tests/Integration/LanguageServerLoggingTests.cs index 309d02523..346bfd8f3 100644 --- a/test/Lsp.Tests/Integration/LanguageServerLoggingTests.cs +++ b/test/Lsp.Tests/Integration/LanguageServerLoggingTests.cs @@ -25,7 +25,7 @@ public LanguageServerLoggingTests(ITestOutputHelper outputHelper) : base(new Jso { } - [RetryFact] + [Fact] public async Task Logs_Are_Sent_To_Client_From_Server() { var logs = new ConcurrentBag(); @@ -63,7 +63,7 @@ public async Task Logs_Are_Sent_To_Client_From_Server() items.Where(z => z.Type == MessageType.Log).Should().HaveCount(2); } - [RetryFact] + [Fact] public async Task Logs_Are_Sent_To_Client_From_Server_Respecting_SetMinimumLevel() { var logs = new ConcurrentBag(); @@ -101,7 +101,7 @@ public async Task Logs_Are_Sent_To_Client_From_Server_Respecting_SetMinimumLevel items.Where(z => z.Type == MessageType.Log).Should().HaveCount(0); } - [RetryFact] + [Fact] public async Task Logs_Are_Sent_To_Client_From_Server_Respecting_TraceLevel() { var logs = new ConcurrentBag(); @@ -139,7 +139,7 @@ public async Task Logs_Are_Sent_To_Client_From_Server_Respecting_TraceLevel() items.Where(z => z.Type == MessageType.Log).Should().HaveCount(0); } - [RetryFact] + [Fact] public async Task Client_Can_Dynamically_Change_Server_Trace_Level_Off_To_Verbose() { var logs = new ConcurrentBag(); @@ -204,7 +204,7 @@ public async Task Client_Can_Dynamically_Change_Server_Trace_Level_Off_To_Verbos } } - [RetryFact] + [Fact] public async Task Client_Can_Dynamically_Change_Server_Trace_Level_Verbose_To_Off() { var logs = new ConcurrentBag(); diff --git a/test/Lsp.Tests/Integration/LogMessageTests.cs b/test/Lsp.Tests/Integration/LogMessageTests.cs index b527a0ece..6a4869a85 100644 --- a/test/Lsp.Tests/Integration/LogMessageTests.cs +++ b/test/Lsp.Tests/Integration/LogMessageTests.cs @@ -23,7 +23,7 @@ public LogMessageTests(ITestOutputHelper outputHelper) : base(new JsonRpcTestOpt private readonly List _receivedMessages = new List(); - [RetryFact] + [Fact] public async Task Should_Log_Messages_Through_Window_Extension_Methods() { var (_, server) = await Initialize(ConfigureClient, ConfigureServer); @@ -52,7 +52,7 @@ public async Task Should_Log_Messages_Through_Window_Extension_Methods() _receivedMessages.Should().Contain(z => z.Type == MessageType.Log).And.Subject.Count(z => z.Type == MessageType.Log).Should().Be(3); } - [RetryFact] + [Fact] public async Task Should_Log_Messages_Through_Server_Extension_Methods() { var (_, server) = await Initialize(ConfigureClient, ConfigureServer); diff --git a/test/Lsp.Tests/Integration/MonikerTests.cs b/test/Lsp.Tests/Integration/MonikerTests.cs index fa34b3444..6b5c37aaf 100644 --- a/test/Lsp.Tests/Integration/MonikerTests.cs +++ b/test/Lsp.Tests/Integration/MonikerTests.cs @@ -26,7 +26,7 @@ public MonikerTests(ITestOutputHelper outputHelper) : base(new JsonRpcTestOption _request = Substitute.For?>>>(); } - [RetryFact] + [Fact] public async Task Should_Get_Monikers() { _request.Invoke(Arg.Any(), Arg.Any()) diff --git a/test/Lsp.Tests/Integration/OverrideHandlerTests.cs b/test/Lsp.Tests/Integration/OverrideHandlerTests.cs index 219cae125..e0e961e2a 100644 --- a/test/Lsp.Tests/Integration/OverrideHandlerTests.cs +++ b/test/Lsp.Tests/Integration/OverrideHandlerTests.cs @@ -24,7 +24,7 @@ public OverrideHandlerTests(ITestOutputHelper testOutputHelper) : base(new JsonR { } - [RetryFact] + [Fact] public async Task Should_Support_Custom_Execute_Command_Handlers() { var (client, _) = await Initialize( @@ -44,7 +44,7 @@ public async Task Should_Support_Custom_Execute_Command_Handlers() response.Should().BeEquivalentTo(JToken.FromObject(new { someValue = "custom" })); } - [RetryFact] + [Fact] public async Task Should_Support_Mixed_Execute_Command_Handlers() { var (client, _) = await Initialize( diff --git a/test/Lsp.Tests/Integration/PartialItemTests.cs b/test/Lsp.Tests/Integration/PartialItemTests.cs index e7d8ea958..bbf7c21d8 100644 --- a/test/Lsp.Tests/Integration/PartialItemTests.cs +++ b/test/Lsp.Tests/Integration/PartialItemTests.cs @@ -24,7 +24,7 @@ public Delegates(ITestOutputHelper testOutputHelper, LanguageProtocolFixture z.Data.Length).Should().ContainInOrder(1, 2, 3); } - [RetryFact(10)] + [Fact] public async Task Should_Behave_Like_An_Observable_Without_Progress_Support() { var response = await Client.SendRequest(new SemanticTokensParams { TextDocument = new TextDocumentIdentifier(@"c:\test.cs") }, CancellationToken); diff --git a/test/Lsp.Tests/Integration/PartialItemsTests.cs b/test/Lsp.Tests/Integration/PartialItemsTests.cs index 78f65a906..d3243a697 100644 --- a/test/Lsp.Tests/Integration/PartialItemsTests.cs +++ b/test/Lsp.Tests/Integration/PartialItemsTests.cs @@ -29,7 +29,7 @@ public Delegates(ITestOutputHelper testOutputHelper, LanguageProtocolFixture z.Command!.Name).Should().ContainInOrder("CodeLens 1", "CodeLens 2", "CodeLens 3"); } - [RetryFact(10, skipOn: SkipOnPlatform.All)] + [Fact] public async Task Should_Behave_Like_An_Observable() { var items = await Client.TextDocument @@ -63,7 +63,7 @@ public async Task Should_Behave_Like_An_Observable() items.Select(z => z.Command!.Name).Should().ContainInOrder("CodeLens 1", "CodeLens 2", "CodeLens 3"); } - [RetryFact(10, skipOn: SkipOnPlatform.All)] + [Fact] public async Task Should_Behave_Like_An_Observable_Without_Progress_Support() { var response = await Client.SendRequest( @@ -120,7 +120,7 @@ public Handlers(ITestOutputHelper testOutputHelper, LanguageProtocolFixture(); diff --git a/test/Lsp.Tests/Integration/ProgressTests.cs b/test/Lsp.Tests/Integration/ProgressTests.cs index bc3adece5..e61bb7e56 100644 --- a/test/Lsp.Tests/Integration/ProgressTests.cs +++ b/test/Lsp.Tests/Integration/ProgressTests.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Reactive; using System.Reactive.Disposables; using System.Reactive.Linq; @@ -26,7 +28,7 @@ private class Data public string Value { get; set; } = "Value"; } - [FactWithSkipOn(SkipOnPlatform.All)] + [Fact] public async Task Should_Send_Progress_From_Server_To_Client() { var token = new ProgressToken(Guid.NewGuid().ToString()); @@ -76,7 +78,7 @@ await Observable.Create( data.Should().ContainInOrder(new[] { "1", "3", "2", "4", "5" }); } - [FactWithSkipOn(SkipOnPlatform.All)] + [Fact] public async Task Should_Send_Progress_From_Client_To_Server() { var token = new ProgressToken(Guid.NewGuid().ToString()); @@ -116,7 +118,10 @@ public async Task Should_Send_Progress_From_Client_To_Server() await Observable.Create( innerObserver => new CompositeDisposable() { - observable.Take(5).Select(z => z.Value).Subscribe(v => innerObserver.OnNext(Unit.Default), innerObserver.OnCompleted), + observable + .Take(5) + .Select(z => z.Value) + .Subscribe(v => innerObserver.OnNext(Unit.Default), innerObserver.OnCompleted), workDoneObservable } ).ToTask(CancellationToken); @@ -126,14 +131,14 @@ await Observable.Create( data.Should().ContainInOrder(new[] { "1", "3", "2", "4", "5" }); } - [FactWithSkipOn(SkipOnPlatform.All)] + [Fact] public void WorkDone_Should_Be_Supported() { Server.WorkDoneManager.IsSupported.Should().BeTrue(); Client.WorkDoneManager.IsSupported.Should().BeTrue(); } - [FactWithSkipOn(SkipOnPlatform.All)] + [Fact] public async Task Should_Support_Creating_Work_Done_From_Sever_To_Client() { var token = new ProgressToken(Guid.NewGuid().ToString()); @@ -195,7 +200,7 @@ public async Task Should_Support_Creating_Work_Done_From_Sever_To_Client() results.Should().ContainInOrder("Begin", "Report 1", "Report 2", "Report 3", "Report 4", "End"); } - [FactWithSkipOn(SkipOnPlatform.All)] + [Fact] public async Task Should_Support_Observing_Work_Done_From_Client_To_Server_Request() { var token = new ProgressToken(Guid.NewGuid().ToString()); @@ -257,14 +262,14 @@ public async Task Should_Support_Observing_Work_Done_From_Client_To_Server_Reque results.Should().ContainInOrder("Begin", "Report 1", "Report 2", "Report 3", "Report 4", "End"); } - [FactWithSkipOn(SkipOnPlatform.All)] + [Fact] public async Task Should_Support_Cancelling_Work_Done_From_Client_To_Server_Request() { var token = new ProgressToken(Guid.NewGuid().ToString()); var workDoneObservable = Client.WorkDoneManager.Monitor(token); - var observable = workDoneObservable.Replay(); - using var _ = observable.Connect(); + var data = new List(); + using var _ = workDoneObservable.Subscribe(x => data.Add(x)); using var workDoneObserver = await Server.WorkDoneManager.Create( token, new WorkDoneProgressBegin { @@ -291,7 +296,7 @@ public async Task Should_Support_Cancelling_Work_Done_From_Client_To_Server_Requ } ); - await observable.Take(3).ToTask(CancellationToken); + await TestHelper.DelayUntil(() => data.Count >= 3, CancellationToken); workDoneObservable.Dispose(); @@ -311,14 +316,16 @@ public async Task Should_Support_Cancelling_Work_Done_From_Client_To_Server_Requ workDoneObserver.OnCompleted(); - var results = await observable.Select( - z => z switch { - WorkDoneProgressBegin begin => begin.Message, - WorkDoneProgressReport begin => begin.Message, - WorkDoneProgressEnd begin => begin.Message, - _ => throw new NotSupportedException() - } - ).ToArray().ToTask(CancellationToken); + var results = data + .Select( + z => z switch { + WorkDoneProgressBegin begin => begin.Message, + WorkDoneProgressReport begin => begin.Message, + WorkDoneProgressEnd begin => begin.Message, + _ => throw new NotSupportedException() + } + ) + .ToArray(); results.Should().ContainInOrder("Begin", "Report 1", "Report 2"); } diff --git a/test/Lsp.Tests/Integration/ProposalTests.cs b/test/Lsp.Tests/Integration/ProposalTests.cs index c0f593d7c..002834c5e 100644 --- a/test/Lsp.Tests/Integration/ProposalTests.cs +++ b/test/Lsp.Tests/Integration/ProposalTests.cs @@ -27,7 +27,7 @@ public ProposalTests(ITestOutputHelper testOutputHelper) : base(new JsonRpcTestO { } - [RetryFact] + [Fact] public async Task Server_Should_Deserialize_Capabilities_As_Proposal_Types() { var (_, server) = await Initialize( @@ -47,7 +47,7 @@ public async Task Server_Should_Deserialize_Capabilities_As_Proposal_Types() server.ClientSettings.Capabilities!.Workspace.Should().BeOfType(); } - [RetryFact] + [Fact] public async Task Client_Should_Deserialize_Capabilities_As_Proposal_Types() { var (client, _) = await Initialize( diff --git a/test/Lsp.Tests/Integration/RenameTests.cs b/test/Lsp.Tests/Integration/RenameTests.cs index 182df71f3..1ea72c999 100644 --- a/test/Lsp.Tests/Integration/RenameTests.cs +++ b/test/Lsp.Tests/Integration/RenameTests.cs @@ -32,7 +32,7 @@ public RenameTests(ITestOutputHelper outputHelper) : base(new JsonRpcTestOptions _rename = Substitute.For>>(); } - [RetryFact] + [Fact] public async Task Should_Handle_Rename_With_No_Value() { _prepareRename.Invoke(Arg.Any(), Arg.Any(), Arg.Any()) @@ -88,7 +88,7 @@ public async Task Should_Handle_Rename_With_No_Value() capability1.Should().BeSameAs(capability2); } - [RetryFact] + [Fact] public async Task Should_Handle_Prepare_Rename_With_No_Value() { _prepareRename.Invoke(Arg.Any(), Arg.Any(), Arg.Any()) @@ -106,7 +106,7 @@ public async Task Should_Handle_Prepare_Rename_With_No_Value() result.Should().BeNull(); } - [RetryFact] + [Fact] public async Task Should_Handle_Prepare_Rename_With_Range() { _prepareRename.Invoke(Arg.Any(), Arg.Any(), Arg.Any()) @@ -135,7 +135,7 @@ public async Task Should_Handle_Prepare_Rename_With_Range() result!.IsRange.Should().BeTrue(); } - [RetryFact] + [Fact] public async Task Should_Handle_Prepare_Rename_With_PlaceholderRange() { _prepareRename.Invoke(Arg.Any(), Arg.Any(), Arg.Any()) @@ -168,7 +168,7 @@ public async Task Should_Handle_Prepare_Rename_With_PlaceholderRange() result!.IsPlaceholderRange.Should().BeTrue(); } - [RetryFact] + [Fact] public async Task Should_Handle_Prepare_Rename_With_DefaultBehavior() { _prepareRename.Invoke(Arg.Any(), Arg.Any(), Arg.Any()) @@ -193,7 +193,7 @@ public async Task Should_Handle_Prepare_Rename_With_DefaultBehavior() result!.IsDefaultBehavior.Should().BeTrue(); } - [RetryFact] + [Fact] public async Task Should_Not_Register_Prepare_Rename() { var (client, _) = await Initialize(ClientOptionsAction, ServerOptionsAction); diff --git a/test/Lsp.Tests/Integration/RequestCancellationTests.cs b/test/Lsp.Tests/Integration/RequestCancellationTests.cs index 59a666983..4e964bbcc 100644 --- a/test/Lsp.Tests/Integration/RequestCancellationTests.cs +++ b/test/Lsp.Tests/Integration/RequestCancellationTests.cs @@ -26,7 +26,7 @@ public RequestCancellationTests(ITestOutputHelper outputHelper) : base(new JsonR { } - [RetryFact] + [Fact] public async Task Should_Cancel_Pending_Requests() { var (client, _) = await Initialize(ConfigureClient, ConfigureServer); @@ -43,7 +43,7 @@ public async Task Should_Cancel_Pending_Requests() action.Should().Throw(); } - [RetryFact] + [Fact] public async Task Should_Abandon_Pending_Requests_For_Text_Changes() { var (client, _) = await Initialize(ConfigureClient, ConfigureServer); @@ -68,7 +68,7 @@ public async Task Should_Abandon_Pending_Requests_For_Text_Changes() action.Should().Throw(); } - [RetryFact] + [Fact] public async Task Should_Cancel_Requests_After_Timeout() { Func action = async () => { @@ -88,7 +88,7 @@ await client.TextDocument.RequestCompletion( action.Should().Throw(); } - [RetryFact] + [Fact] public void Should_Cancel_Requests_After_Timeout_without_Content_Modified() { Func action = async () => { @@ -108,7 +108,7 @@ await client.TextDocument.RequestCompletion( action.Should().Throw(); } - [RetryFact] + [Fact] public async Task Can_Publish_Diagnostics_Delayed() { var (_, server) = await Initialize( diff --git a/test/Lsp.Tests/Integration/ShowMessageTests.cs b/test/Lsp.Tests/Integration/ShowMessageTests.cs index 09ba45fbb..92cd016af 100644 --- a/test/Lsp.Tests/Integration/ShowMessageTests.cs +++ b/test/Lsp.Tests/Integration/ShowMessageTests.cs @@ -24,7 +24,7 @@ public ShowMessageTests(ITestOutputHelper outputHelper) : base(new JsonRpcTestOp private readonly List _receivedMessages = new List(); - [RetryFact] + [Fact] public async Task Should_Show_Messages_Through_Window_Extension_Methods() { var (_, server) = await Initialize(ConfigureClient, ConfigureServer); @@ -53,7 +53,7 @@ public async Task Should_Show_Messages_Through_Window_Extension_Methods() _receivedMessages.Should().Contain(z => z.Type == MessageType.Log).And.Subject.Count(z => z.Type == MessageType.Log).Should().Be(3); } - [RetryFact] + [Fact] public async Task Should_Show_Messages_Through_Server_Extension_Methods() { var (_, server) = await Initialize(ConfigureClient, ConfigureServer); diff --git a/test/Lsp.Tests/Integration/TypedCallHierarchyTests.cs b/test/Lsp.Tests/Integration/TypedCallHierarchyTests.cs index d98e35252..252524f53 100644 --- a/test/Lsp.Tests/Integration/TypedCallHierarchyTests.cs +++ b/test/Lsp.Tests/Integration/TypedCallHierarchyTests.cs @@ -30,7 +30,7 @@ public TypedCallHierarchyTests(ITestOutputHelper outputHelper) : base(new JsonRp { } - [RetryFact] + [Fact] public async Task Should_Aggregate_With_All_Related_Handlers() { var incomingHandlerA = Substitute.For, Task?>>>(); @@ -106,7 +106,7 @@ public async Task Should_Aggregate_With_All_Related_Handlers() outgoingHandlerB.Received(1).Invoke(Arg.Any>()); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Data_Capability() { var incomingHandler = Substitute.For, Task?>>>(); @@ -152,7 +152,7 @@ public async Task Should_Resolve_With_Data_Capability() outgoingHandler.Received(1).Invoke(Arg.Any>()); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Partial_Data_Capability() { var incomingHandler = Substitute.For, IObserver>>>(); diff --git a/test/Lsp.Tests/Integration/TypedCodeActionTests.cs b/test/Lsp.Tests/Integration/TypedCodeActionTests.cs index a0245b1f4..02373175d 100644 --- a/test/Lsp.Tests/Integration/TypedCodeActionTests.cs +++ b/test/Lsp.Tests/Integration/TypedCodeActionTests.cs @@ -28,7 +28,7 @@ public TypedCodeActionTests(ITestOutputHelper outputHelper) : base(new JsonRpcTe { } - [RetryFact] + [Fact] public async Task Should_Aggregate_With_All_Related_Handlers() { var (client, _) = await Initialize( @@ -157,7 +157,7 @@ public async Task Should_Aggregate_With_All_Related_Handlers() actions.Length.Should().Be(3); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Data_Capability() { var (client, _) = await Initialize( @@ -203,7 +203,7 @@ public async Task Should_Resolve_With_Data_Capability() item.CodeAction!.Command!.Name.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Partial_Data_Capability() { var (client, _) = await Initialize( @@ -248,7 +248,7 @@ public async Task Should_Resolve_With_Partial_Data_Capability() item.CodeAction!.Command!.Name.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Data_CancellationToken() { var (client, _) = await Initialize( @@ -294,7 +294,7 @@ public async Task Should_Resolve_With_Data_CancellationToken() item.CodeAction!.Command!.Name.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Partial_Data_CancellationToken() { var (client, _) = await Initialize( @@ -339,7 +339,7 @@ public async Task Should_Resolve_With_Partial_Data_CancellationToken() item.CodeAction!.Command!.Name.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Data() { var (client, _) = await Initialize( @@ -385,7 +385,7 @@ public async Task Should_Resolve_With_Data() item.CodeAction!.Command!.Name.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Partial_Data() { var (client, _) = await Initialize( @@ -431,7 +431,7 @@ public async Task Should_Resolve_With_Partial_Data() } - [RetryFact] + [Fact] public async Task Should_Resolve_Capability() { var (client, _) = await Initialize( @@ -467,7 +467,7 @@ public async Task Should_Resolve_Capability() item.CodeAction!.Command!.Name.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_Partial_Capability() { var (client, _) = await Initialize( @@ -502,7 +502,7 @@ public async Task Should_Resolve_Partial_Capability() item.CodeAction!.Command!.Name.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_CancellationToken() { var (client, _) = await Initialize( @@ -538,7 +538,7 @@ public async Task Should_Resolve_CancellationToken() item.CodeAction!.Command!.Name.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_Partial_CancellationToken() { var (client, _) = await Initialize( @@ -573,7 +573,7 @@ public async Task Should_Resolve_Partial_CancellationToken() item.CodeAction!.Command!.Name.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve() { var (client, _) = await Initialize( @@ -609,7 +609,7 @@ public async Task Should_Resolve() item.CodeAction!.Command!.Name.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_Partial() { var (client, _) = await Initialize( diff --git a/test/Lsp.Tests/Integration/TypedCodeLensTests.cs b/test/Lsp.Tests/Integration/TypedCodeLensTests.cs index 39e84ff7f..7e70e8bf0 100644 --- a/test/Lsp.Tests/Integration/TypedCodeLensTests.cs +++ b/test/Lsp.Tests/Integration/TypedCodeLensTests.cs @@ -29,7 +29,7 @@ public TypedCodeLensTests(ITestOutputHelper outputHelper) : base(new JsonRpcTest { } - [RetryFact] + [Fact] public async Task Should_Aggregate_With_All_Related_Handlers() { var (client, _) = await Initialize( @@ -142,7 +142,7 @@ public async Task Should_Aggregate_With_All_Related_Handlers() lens.Length.Should().Be(3); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Data_Capability() { var (client, _) = await Initialize( @@ -187,7 +187,7 @@ public async Task Should_Resolve_With_Data_Capability() item.Command!.Name.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Partial_Data_Capability() { var (client, _) = await Initialize( @@ -231,7 +231,7 @@ public async Task Should_Resolve_With_Partial_Data_Capability() item.Command!.Name.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Data_CancellationToken() { var (client, _) = await Initialize( @@ -276,7 +276,7 @@ public async Task Should_Resolve_With_Data_CancellationToken() item.Command!.Name.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Partial_Data_CancellationToken() { var (client, _) = await Initialize( @@ -320,7 +320,7 @@ public async Task Should_Resolve_With_Partial_Data_CancellationToken() item.Command!.Name.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Data() { var (client, _) = await Initialize( @@ -365,7 +365,7 @@ public async Task Should_Resolve_With_Data() item.Command!.Name.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Partial_Data() { var (client, _) = await Initialize( @@ -410,7 +410,7 @@ public async Task Should_Resolve_With_Partial_Data() } - [RetryFact] + [Fact] public async Task Should_Resolve_Capability() { var (client, _) = await Initialize( @@ -445,7 +445,7 @@ public async Task Should_Resolve_Capability() item.Command!.Name.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_Partial_Capability() { var (client, _) = await Initialize( @@ -479,7 +479,7 @@ public async Task Should_Resolve_Partial_Capability() item.Command!.Name.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_CancellationToken() { var (client, _) = await Initialize( @@ -514,7 +514,7 @@ public async Task Should_Resolve_CancellationToken() item.Command!.Name.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_Partial_CancellationToken() { var (client, _) = await Initialize( @@ -548,7 +548,7 @@ public async Task Should_Resolve_Partial_CancellationToken() item.Command!.Name.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve() { var (client, _) = await Initialize( @@ -583,7 +583,7 @@ public async Task Should_Resolve() item.Command!.Name.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_Partial() { var (client, _) = await Initialize( diff --git a/test/Lsp.Tests/Integration/TypedCompletionTests.cs b/test/Lsp.Tests/Integration/TypedCompletionTests.cs index 957174ddc..e39147815 100644 --- a/test/Lsp.Tests/Integration/TypedCompletionTests.cs +++ b/test/Lsp.Tests/Integration/TypedCompletionTests.cs @@ -29,7 +29,7 @@ public TypedCompletionTests(ITestOutputHelper outputHelper) : base(new JsonRpcTe { } - [RetryFact] + [Fact] public async Task Should_Aggregate_With_All_Related_Handlers() { var (client, _) = await Initialize( @@ -142,7 +142,7 @@ public async Task Should_Aggregate_With_All_Related_Handlers() lens.Length.Should().Be(3); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Data_Capability() { var (client, _) = await Initialize( @@ -191,7 +191,7 @@ completionItem with { item.Detail.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Partial_Data_Capability() { var (client, _) = await Initialize( @@ -234,7 +234,7 @@ public async Task Should_Resolve_With_Partial_Data_Capability() item.Detail.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Data_CancellationToken() { var (client, _) = await Initialize( @@ -278,7 +278,7 @@ public async Task Should_Resolve_With_Data_CancellationToken() item.Detail.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Partial_Data_CancellationToken() { var (client, _) = await Initialize( @@ -321,7 +321,7 @@ public async Task Should_Resolve_With_Partial_Data_CancellationToken() item.Detail.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Data() { var (client, _) = await Initialize( @@ -365,7 +365,7 @@ public async Task Should_Resolve_With_Data() item.Detail.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Partial_Data() { var (client, _) = await Initialize( @@ -409,7 +409,7 @@ public async Task Should_Resolve_With_Partial_Data() } - [RetryFact] + [Fact] public async Task Should_Resolve_Capability() { var (client, _) = await Initialize( @@ -441,7 +441,7 @@ public async Task Should_Resolve_Capability() item.Detail.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_Partial_Capability() { var (client, _) = await Initialize( @@ -472,7 +472,7 @@ public async Task Should_Resolve_Partial_Capability() item.Detail.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_CancellationToken() { var (client, _) = await Initialize( @@ -504,7 +504,7 @@ public async Task Should_Resolve_CancellationToken() item.Detail.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_Partial_CancellationToken() { var (client, _) = await Initialize( @@ -535,7 +535,7 @@ public async Task Should_Resolve_Partial_CancellationToken() item.Detail.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve() { var (client, _) = await Initialize( @@ -567,7 +567,7 @@ public async Task Should_Resolve() item.Detail.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_Partial() { var (client, _) = await Initialize( diff --git a/test/Lsp.Tests/Integration/TypedDocumentLinkTests.cs b/test/Lsp.Tests/Integration/TypedDocumentLinkTests.cs index de3da02c4..0cd4ebd54 100644 --- a/test/Lsp.Tests/Integration/TypedDocumentLinkTests.cs +++ b/test/Lsp.Tests/Integration/TypedDocumentLinkTests.cs @@ -27,7 +27,7 @@ public TypedDocumentLinkTests(ITestOutputHelper outputHelper) : base(new JsonRpc { } - [RetryFact] + [Fact] public async Task Should_Aggregate_With_All_Related_Handlers() { var (client, _) = await Initialize( @@ -136,7 +136,7 @@ public async Task Should_Aggregate_With_All_Related_Handlers() lens.Length.Should().Be(3); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Data_Capability() { var (client, _) = await Initialize( @@ -177,7 +177,7 @@ public async Task Should_Resolve_With_Data_Capability() item.Tooltip.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Partial_Data_Capability() { var (client, _) = await Initialize( @@ -217,7 +217,7 @@ public async Task Should_Resolve_With_Partial_Data_Capability() item.Tooltip.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Data_CancellationToken() { var (client, _) = await Initialize( @@ -258,7 +258,7 @@ public async Task Should_Resolve_With_Data_CancellationToken() item.Tooltip.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Partial_Data_CancellationToken() { var (client, _) = await Initialize( @@ -298,7 +298,7 @@ public async Task Should_Resolve_With_Partial_Data_CancellationToken() item.Tooltip.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Data() { var (client, _) = await Initialize( @@ -339,7 +339,7 @@ public async Task Should_Resolve_With_Data() item.Tooltip.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_With_Partial_Data() { var (client, _) = await Initialize( @@ -380,7 +380,7 @@ public async Task Should_Resolve_With_Partial_Data() } - [RetryFact] + [Fact] public async Task Should_Resolve_Capability() { var (client, _) = await Initialize( @@ -411,7 +411,7 @@ public async Task Should_Resolve_Capability() item.Tooltip.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_Partial_Capability() { var (client, _) = await Initialize( @@ -441,7 +441,7 @@ public async Task Should_Resolve_Partial_Capability() item.Tooltip.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_CancellationToken() { var (client, _) = await Initialize( @@ -472,7 +472,7 @@ public async Task Should_Resolve_CancellationToken() item.Tooltip.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_Partial_CancellationToken() { var (client, _) = await Initialize( @@ -502,7 +502,7 @@ public async Task Should_Resolve_Partial_CancellationToken() item.Tooltip.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve() { var (client, _) = await Initialize( @@ -533,7 +533,7 @@ public async Task Should_Resolve() item.Tooltip.Should().Be("resolved"); } - [RetryFact] + [Fact] public async Task Should_Resolve_Partial() { var (client, _) = await Initialize( diff --git a/test/Lsp.Tests/Integration/WorkspaceFolderTests.cs b/test/Lsp.Tests/Integration/WorkspaceFolderTests.cs index 38361ea77..2c716f85c 100644 --- a/test/Lsp.Tests/Integration/WorkspaceFolderTests.cs +++ b/test/Lsp.Tests/Integration/WorkspaceFolderTests.cs @@ -33,7 +33,7 @@ public WorkspaceFolderTests(ITestOutputHelper outputHelper) : base( { } - [RetryFact] + [Fact] public async Task Should_Disable_If_Not_Supported() { var (_, server) = await Initialize( @@ -47,14 +47,14 @@ public async Task Should_Disable_If_Not_Supported() folders.Should().BeEmpty(); } - [RetryFact] + [Fact] public async Task Should_Enable_If_Supported() { var (_, server) = await Initialize(ConfigureClient, ConfigureServer); server.WorkspaceFolderManager.IsSupported.Should().Be(true); } - [RetryFact] + [Fact] public async Task Should_Add_A_Workspace_Folder() { var (client, server) = await Initialize(ConfigureClient, ConfigureServer); @@ -71,7 +71,7 @@ public async Task Should_Add_A_Workspace_Folder() folders[0].Folder.Name.Should().Be(nameof(Should_Add_A_Workspace_Folder)); } - [RetryFact] + [Fact] public async Task Should_Allow_Null_Response() { var (client, server) = await Initialize( @@ -84,7 +84,7 @@ public async Task Should_Allow_Null_Response() a.Should().NotThrow(); } - [RetryFact] + [Fact] public async Task Should_Have_Workspace_Folder_At_Startup() { var (_, server) = await Initialize(options => { options.WithWorkspaceFolder("/abcd/", nameof(Should_Have_Workspace_Folder_At_Startup)); }, ConfigureServer); @@ -93,7 +93,7 @@ public async Task Should_Have_Workspace_Folder_At_Startup() folder.Name.Should().Be(nameof(Should_Have_Workspace_Folder_At_Startup)); } - [RetryFact] + [Fact] public async Task Should_Remove_Workspace_Folder_by_name() { var (client, server) = await Initialize(options => { options.WithWorkspaceFolder("/abcd/", nameof(Should_Remove_Workspace_Folder_by_name)); }, ConfigureServer); @@ -113,7 +113,7 @@ public async Task Should_Remove_Workspace_Folder_by_name() folders[0].Folder.Name.Should().Be(nameof(Should_Remove_Workspace_Folder_by_name)); } - [RetryFact] + [Fact] public async Task Should_Remove_Workspace_Folder_by_uri() { var (client, server) = await Initialize(options => { options.WithWorkspaceFolder("/abcd/", nameof(Should_Remove_Workspace_Folder_by_uri)); }, ConfigureServer); @@ -133,7 +133,7 @@ public async Task Should_Remove_Workspace_Folder_by_uri() folders[0].Folder.Name.Should().Be(nameof(Should_Remove_Workspace_Folder_by_uri)); } - [RetryFact] + [Fact] public async Task Should_Handle_Null_Workspace_Folders() { var workspaceLanguageServer = Substitute.For(); @@ -154,7 +154,7 @@ public async Task Should_Handle_Null_Workspace_Folders() await started.OnStarted(languageServer, CancellationToken); } - [RetryFact] + [Fact] public async Task Should_Handle_Null_Workspace_Folders_On_Refresh() { var workspaceLanguageServer = Substitute.For(); diff --git a/test/TestingUtils/RetryFactDiscoverer.cs b/test/TestingUtils/RetryFactDiscoverer.cs index 7db21d8ff..280837c86 100644 --- a/test/TestingUtils/RetryFactDiscoverer.cs +++ b/test/TestingUtils/RetryFactDiscoverer.cs @@ -23,13 +23,13 @@ public IEnumerable Discover(ITestFrameworkDiscoveryOptions disco { testCase = new ExecutionErrorTestCase(_messageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod, - "[RetryFact] methods are not allowed to have parameters. Did you mean to use [RetryTheory]?"); + "[Fact]//[RetryFact] methods are not allowed to have parameters. Did you mean to use [RetryTheory]?"); } else if (testMethod.Method.IsGenericMethodDefinition) { testCase = new ExecutionErrorTestCase(_messageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod, - "[RetryFact] methods are not allowed to be generic."); + "[Fact]//[RetryFact] methods are not allowed to be generic."); } else {