/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/ServerDisconnectTests.cs
84 строки
3 KB
Copilot
Fix race condition in ServerThrowsOnStreamCorruption test (#84110)
18 июн 2026, 22:50
Не верифицирован
18 июн 2026, 22:50
bbd34b8
Код
Авторство
О чём код?
// 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.Text; using Roslyn.LanguageServer.Protocol; using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests; public sealed class ServerDisconnectTests(ITestOutputHelper testOutputHelper) : AbstractLanguageServerHostTests(testOutputHelper) { [Fact] public async Task ServerExitsCleanlyOnIOException() { var server = await CreateLanguageServerAsync(); // Simulate the server getting an EndOfStreamException(IOException) when reading from the JSON-RPC stream. server.ClientToServerPipe.Writer.Complete(new EndOfStreamException()); // Server should exit cleanly without throwing. await server.ServerExitTask; } [Fact] public async Task ServerExitsCleanlyWhenClientDisconnects() { var server = await CreateLanguageServerAsync(); // Simulate the client disconnecting abruptly. server.ClientToServerPipe.Writer.Complete(); server.ServerToClientPipe.Reader.Complete(); // Server should exit cleanly without throwing. await server.ServerExitTask; } [Fact] public async Task ServerExitsOnExitNotificationWithoutClosingTransport() { // Verify that the server terminates after it receives the exit notification, even if the client // never closes its end of the transport. var server = await CreateLanguageServerAsync(); await server.ExecuteRequestAsync<object, object>(Methods.ShutdownName, new object(), CancellationToken.None); await server.ExecuteNotification0Async(Methods.ExitName); // Server should exit even though we never complete the client->server pipe. await server.ServerExitTask; } [Fact] public async Task ServerThrowsOnStreamCorruption() { var server = await CreateLanguageServerAsync(); // Write a valid JSON-RPC header with a corrupt (non-JSON) body to cause a deserialization error. // Both the header and body must be written atomically (without awaiting between them) to avoid a // race condition where the server disconnects between the two writes causing _clientRpc to // asynchronously complete the pipe writer, making the second write throw. var garbageBody = Encoding.UTF8.GetBytes("this is not valid json!!"); var header = Encoding.ASCII.GetBytes($"Content-Length: {garbageBody.Length}\r\n\r\n"); var message = new byte[header.Length + garbageBody.Length]; header.CopyTo(message, 0); garbageBody.CopyTo(message, header.Length); await server.ClientToServerPipe.Writer.WriteAsync(message); server.ClientToServerPipe.Writer.Complete(); // Corruption is not a clean disconnect - the server should propagate the error. var exception = await Assert.ThrowsAnyAsync<Exception>(() => server.ServerExitTask); Assert.NotNull(exception); } [Fact] public async Task ServerThrowsOnUnexpectedException() { var server = await CreateLanguageServerAsync(); server.ClientToServerPipe.Writer.Complete(new InvalidOperationException("Something went wrong")); // Unexpected exceptions should propagate to WaitForExitAsync callers. await Assert.ThrowsAsync<InvalidOperationException>(() => server.ServerExitTask); } }