RequestOrderingTests.cs 9.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
11
using Microsoft.CodeAnalysis.CSharp.Syntax;
12 13 14 15 16 17 18 19 20 21 22 23 24 25
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServer.Handler;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
using LSP = Microsoft.VisualStudio.LanguageServer.Protocol;

namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.RequestOrdering
{
    public partial class RequestOrderingTests : AbstractLanguageServerProtocolTests
    {
        protected override TestComposition Composition => base.Composition
            .AddParts(typeof(MutatingRequestHandler))
26 27 28
            .AddParts(typeof(NonMutatingRequestHandler))
            .AddParts(typeof(FailingRequestHandler))
            .AddParts(typeof(FailingMutatingRequestHandler));
29 30

        [Fact]
D
Doc  
David Wengier 已提交
31
        public async Task MutatingRequestsDontOverlap()
32 33
        {
            var requests = new[] {
D
Rename  
David Wengier 已提交
34 35 36
                new TestRequest(MutatingRequestHandler.MethodName),
                new TestRequest(MutatingRequestHandler.MethodName),
                new TestRequest(MutatingRequestHandler.MethodName),
37 38 39 40 41 42 43 44 45 46
            };

            var responses = await TestAsync(requests);

            // Every request should have started at or after the one before it
            Assert.True(responses[1].StartTime >= responses[0].EndTime);
            Assert.True(responses[2].StartTime >= responses[1].EndTime);
        }

        [Fact]
D
Doc  
David Wengier 已提交
47
        public async Task NonMutatingRequestsOverlap()
48 49
        {
            var requests = new[] {
D
Rename  
David Wengier 已提交
50 51 52
                new TestRequest(NonMutatingRequestHandler.MethodName),
                new TestRequest(NonMutatingRequestHandler.MethodName),
                new TestRequest(NonMutatingRequestHandler.MethodName),
53 54 55 56 57 58 59 60 61 62
            };

            var responses = await TestAsync(requests);

            // Every request should have started immediately, without waiting
            Assert.True(responses[1].StartTime < responses[0].EndTime);
            Assert.True(responses[2].StartTime < responses[1].EndTime);
        }

        [Fact]
D
Doc  
David Wengier 已提交
63
        public async Task NonMutatingWaitsForMutating()
64 65
        {
            var requests = new[] {
D
Rename  
David Wengier 已提交
66 67 68
                new TestRequest(MutatingRequestHandler.MethodName),
                new TestRequest(NonMutatingRequestHandler.MethodName),
                new TestRequest(NonMutatingRequestHandler.MethodName),
69 70 71 72
            };

            var responses = await TestAsync(requests);

D
Doc  
David Wengier 已提交
73
            // The non mutating tasks should have waited for the first task to finish
74 75
            Assert.True(responses[1].StartTime >= responses[0].EndTime);
            Assert.True(responses[2].StartTime >= responses[0].EndTime);
D
Doc  
David Wengier 已提交
76
            // The non mutating requests shouldn't have waited for each other
77 78 79 80
            Assert.True(responses[1].StartTime < responses[2].EndTime);
            Assert.True(responses[2].StartTime < responses[1].EndTime);
        }

D
David Wengier 已提交
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
        [Fact]
        public async Task MutatingDoesntWaitForNonMutating()
        {
            var requests = new[] {
                new TestRequest(NonMutatingRequestHandler.MethodName),
                new TestRequest(NonMutatingRequestHandler.MethodName),
                new TestRequest(MutatingRequestHandler.MethodName),
            };

            var responses = await TestAsync(requests);

            // All tasks should start without waiting for any to finish
            Assert.True(responses[1].StartTime < responses[0].EndTime);
            Assert.True(responses[2].StartTime < responses[0].EndTime);
            Assert.True(responses[1].StartTime < responses[2].EndTime);
            Assert.True(responses[2].StartTime < responses[1].EndTime);
        }

99
        [Fact]
D
Doc  
David Wengier 已提交
100
        public async Task NonMutatingOperatesOnTheRightSolutions()
101 102
        {
            var requests = new[] {
D
Rename  
David Wengier 已提交
103 104 105 106 107
                new TestRequest(NonMutatingRequestHandler.MethodName),
                new TestRequest(NonMutatingRequestHandler.MethodName),
                new TestRequest(MutatingRequestHandler.MethodName),
                new TestRequest(NonMutatingRequestHandler.MethodName),
                new TestRequest(NonMutatingRequestHandler.MethodName),
108 109 110 111
            };

            var responses = await TestAsync(requests);

D
Doc  
David Wengier 已提交
112
            // first two tasks should have kicked off without waiting
113 114 115
            Assert.True(responses[0].StartTime < responses[1].EndTime);
            Assert.True(responses[1].StartTime < responses[0].EndTime);

116 117 118 119 120
            // The mutating task should have kicked off without waiting for those to finish
            Assert.True(responses[2].StartTime < responses[1].EndTime);
            Assert.True(responses[2].StartTime < responses[0].EndTime);

            // The last two tasks should have waited for the mutating task
121 122 123
            Assert.True(responses[3].StartTime >= responses[2].EndTime);
            Assert.True(responses[4].StartTime >= responses[2].EndTime);

124 125
            // The last two should have operated on different solutions than the first three
            Assert.NotEqual(responses[3].Solution.WorkspaceVersion, responses[0].Solution.WorkspaceVersion);
126
            Assert.NotEqual(responses[3].Solution.WorkspaceVersion, responses[1].Solution.WorkspaceVersion);
127 128
            Assert.NotEqual(responses[3].Solution.WorkspaceVersion, responses[2].Solution.WorkspaceVersion);
            Assert.Equal(responses[3].Solution.WorkspaceVersion, responses[3].Solution.WorkspaceVersion);
129 130
        }

131 132 133 134
        [Fact]
        public async Task ThrowingTaskDoesntBringDownQueue()
        {
            var requests = new[] {
D
Rename  
David Wengier 已提交
135 136 137 138
                new TestRequest(FailingRequestHandler.MethodName),
                new TestRequest(NonMutatingRequestHandler.MethodName),
                new TestRequest(MutatingRequestHandler.MethodName),
                new TestRequest(NonMutatingRequestHandler.MethodName),
139 140 141 142
            };

            var waitables = StartTestRun(requests);

D
Doc  
David Wengier 已提交
143
            // first task should fail
144 145
            await Assert.ThrowsAsync<InvalidOperationException>(async () => await waitables[0]);

D
Doc  
David Wengier 已提交
146
            // remaining tasks should have executed normally
147 148 149 150 151 152 153 154 155 156
            var responses = await Task.WhenAll(waitables.Skip(1));

            Assert.Empty(responses.Where(r => r.StartTime == default));
            Assert.All(responses, r => Assert.True(r.EndTime > r.StartTime));
        }

        [Fact]
        public async Task ThrowingMutableTaskDoesntBringDownQueue()
        {
            var requests = new[] {
D
Rename  
David Wengier 已提交
157 158 159 160
                new TestRequest(FailingMutatingRequestHandler.MethodName),
                new TestRequest(NonMutatingRequestHandler.MethodName),
                new TestRequest(MutatingRequestHandler.MethodName),
                new TestRequest(NonMutatingRequestHandler.MethodName),
161 162 163 164
            };

            var waitables = StartTestRun(requests);

D
Doc  
David Wengier 已提交
165
            // first task should fail
166 167
            await Assert.ThrowsAsync<InvalidOperationException>(async () => await waitables[0]);

D
Doc  
David Wengier 已提交
168
            // remaining tasks should have executed normally
169 170 171 172 173 174 175 176 177 178
            var responses = await Task.WhenAll(waitables.Skip(1));

            Assert.Empty(responses.Where(r => r.StartTime == default));
            Assert.All(responses, r => Assert.True(r.EndTime > r.StartTime));
        }

        [Fact]
        public async Task ThrowingMutableTaskDoesntMutateTheSolution()
        {
            var requests = new[] {
D
Rename  
David Wengier 已提交
179 180 181
                new TestRequest(NonMutatingRequestHandler.MethodName),
                new TestRequest(FailingMutatingRequestHandler.MethodName),
                new TestRequest(NonMutatingRequestHandler.MethodName),
182 183 184 185
            };

            var waitables = StartTestRun(requests);

D
Doc  
David Wengier 已提交
186
            // second task should have failed
187 188 189 190
            await Assert.ThrowsAsync<InvalidOperationException>(async () => await waitables[1]);

            var responses = await Task.WhenAll(waitables.Where(t => !t.IsFaulted));

D
Doc  
David Wengier 已提交
191
            // First and last tasks use the same solution because the middle request failed
192 193
            // Note the last task is the _second_ item in responses because it only contains the successful responses
            Assert.Equal(responses[0].Solution.WorkspaceVersion, responses[1].Solution.WorkspaceVersion);
194 195
        }

D
Rename  
David Wengier 已提交
196
        private async Task<TestResponse[]> TestAsync(TestRequest[] requests)
197 198 199 200 201 202 203 204 205 206 207 208
        {
            var waitables = StartTestRun(requests);

            var responses = await Task.WhenAll(waitables);

            // Sanity checks to ensure test handlers aren't doing something wacky, making future checks invalid
            Assert.Empty(responses.Where(r => r.StartTime == default));
            Assert.All(responses, r => Assert.True(r.EndTime > r.StartTime));

            return responses;
        }

D
Rename  
David Wengier 已提交
209
        private List<Task<TestResponse>> StartTestRun(TestRequest[] requests)
210 211 212 213 214 215 216
        {
            using var workspace = CreateTestWorkspace("class C { }", out _);
            var solution = workspace.CurrentSolution;

            var languageServer = GetLanguageServer(solution);
            var clientCapabilities = new LSP.ClientCapabilities();

D
Rename  
David Wengier 已提交
217
            var waitables = new List<Task<TestResponse>>();
218 219 220 221
            var order = 1;
            foreach (var request in requests)
            {
                request.RequestOrder = order++;
D
Rename  
David Wengier 已提交
222
                waitables.Add(languageServer.ExecuteRequestAsync<TestRequest, TestResponse>(request.MethodName, request, clientCapabilities, null, CancellationToken.None));
223 224
            }

225
            return waitables;
226 227 228
        }
    }
}