/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/Server/VBCSCompilerTests/ServerUtil.cs
223 строки
9 KB
Copilot
Add timeout and log arguments to VBCSCompiler
27 мар 2026, 19:25
Не верифицирован
27 мар 2026, 19:25
210abd1
Код
Авторство
О чём код?
// 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. #nullable disable extern alias csc; extern alias vbc; using Microsoft.CodeAnalysis.CommandLine; using System; using System.IO; using System.IO.Pipes; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Moq; using Xunit; using System.Collections.Concurrent; using System.Collections.Immutable; namespace Microsoft.CodeAnalysis.CompilerServer.UnitTests { internal static class ProtocolUtil { internal static readonly BuildRequest EmptyCSharpBuildRequest = new BuildRequest( RequestLanguage.CSharpCompile, BuildProtocolConstants.GetCommitHash(), ImmutableArray<BuildRequest.Argument>.Empty); internal static readonly BuildRequest EmptyBasicBuildRequest = new BuildRequest( RequestLanguage.VisualBasicCompile, BuildProtocolConstants.GetCommitHash(), ImmutableArray<BuildRequest.Argument>.Empty); internal static readonly BuildResponse EmptyBuildResponse = new CompletedBuildResponse( returnCode: 0, utf8output: false, output: string.Empty); internal static BuildRequest CreateEmptyCSharp(string workingDirectory, string tempDirectory = null) => BuildRequest.Create( RequestLanguage.CSharpCompile, Array.Empty<string>(), workingDirectory, tempDirectory ?? Path.GetTempPath(), compilerHash: BuildProtocolConstants.GetCommitHash()); internal static BuildRequest CreateEmptyCSharpWithKeepAlive(TimeSpan keepAlive, string workingDirectory, string tempDirectory = null) => BuildRequest.Create( RequestLanguage.CSharpCompile, Array.Empty<string>(), workingDirectory, tempDirectory ?? Path.GetTempPath(), compilerHash: BuildProtocolConstants.GetCommitHash(), keepAlive: keepAlive.TotalSeconds.ToString()); } internal sealed class ServerData : IDisposable { internal CancellationTokenSource CancellationTokenSource { get; } internal Task<TestableDiagnosticListener> ServerTask { get; } internal string PipeName { get; } internal ICompilerServerLogger Logger { get; } private ServerData(CancellationTokenSource cancellationTokenSource, string pipeName, ICompilerServerLogger logger, Task<TestableDiagnosticListener> serverTask) { CancellationTokenSource = cancellationTokenSource; PipeName = pipeName; Logger = logger; ServerTask = serverTask; } /// <summary> /// Create a new server at the specified pipe name. This function will return immediately /// after creating the server and does _not_ wait for it to begin listening /// </summary> internal static ServerData Create( ICompilerServerLogger logger, string pipeName = null, ICompilerServerHost compilerServerHost = null, IClientConnectionHost clientConnectionHost = null, TimeSpan? keepAlive = null) { // The total pipe path must be < 92 characters on Unix, so trim this down to 10 chars pipeName ??= ServerUtil.GetPipeName(); compilerServerHost ??= BuildServerController.CreateCompilerServerHost(logger); clientConnectionHost ??= BuildServerController.CreateClientConnectionHost(pipeName, logger); keepAlive ??= Timeout.InfiniteTimeSpan; var listener = new TestableDiagnosticListener(); var serverListenSource = new TaskCompletionSource<bool>(); var cts = new CancellationTokenSource(); var mutexName = BuildServerConnection.GetServerMutexName(pipeName); var task = Task.Run(() => { BuildServerController.CreateAndRunServer( pipeName, compilerServerHost, clientConnectionHost, listener, keepAlive: keepAlive, cancellationToken: cts.Token); return listener; }); return new ServerData(cts, pipeName, logger, task); } /// <summary> /// This function will wait until the server is either listening on the named pipe or /// if it has exited. /// </summary> internal async Task WaitForServerAsync() { // The contract of this function is that it will return once the server has started. Spin here until // we can verify the server has started or simply failed to start. var mutexName = BuildServerConnection.GetServerMutexName(PipeName); while (BuildServerConnection.WasServerMutexOpen(mutexName) != true && !ServerTask.IsCompleted) { await Task.Yield(); } } internal Task<BuildResponse> SendAsync(BuildRequest request, CancellationToken cancellationToken = default) => BuildServerConnection.RunServerBuildRequestAsync( request, PipeName, timeoutOverride: Timeout.Infinite, tryCreateServerFunc: (_, _) => false, Logger, cancellationToken); internal async Task<int> SendShutdownAsync(CancellationToken cancellationToken = default) { var response = await SendAsync(BuildRequest.CreateShutdown(), cancellationToken).ConfigureAwait(false); return ((ShutdownBuildResponse)response).ServerProcessId; } internal async Task<TestableDiagnosticListener> Complete() { CancellationTokenSource.Cancel(); return await ServerTask; } public void Dispose() { if (!CancellationTokenSource.IsCancellationRequested) { CancellationTokenSource.Cancel(); } ServerTask.Wait(); Assert.True(NamedPipeTestUtil.IsPipeFullyClosed(PipeName)); } } internal static class ServerUtil { internal static string DefaultClientDirectory { get; } = Path.GetDirectoryName(typeof(BuildClientTests).Assembly.Location); internal static string DefaultSdkDirectory { get; } = BuildClient.GetSystemSdkDirectory(); internal static BuildPaths CreateBuildPaths(string workingDir, string tempDir) { return new BuildPaths( clientDir: DefaultClientDirectory, workingDir: workingDir, sdkDir: DefaultSdkDirectory, tempDir: tempDir); } internal static string GetPipeName() => Guid.NewGuid().ToString().Substring(0, 10); internal static async Task<ServerData> CreateServer( ICompilerServerLogger logger, string pipeName = null, ICompilerServerHost compilerServerHost = null, IClientConnectionHost clientConnectionHost = null, TimeSpan? keepAlive = null) { var serverData = ServerData.Create(logger, pipeName, compilerServerHost, clientConnectionHost, keepAlive); await serverData.WaitForServerAsync(); return serverData; } internal static BuildClient CreateBuildClient( RequestLanguage language, ICompilerServerLogger logger) { // Create a client to run the build. Infinite timeout is used to account for the // case where these tests are run under extreme load. In high load scenarios the // client will correctly drop down to a local compilation if the server doesn't respond // fast enough. CompileOnServerFunc compileOnServerFunc = (request, pipeName, cancellationToken) => BuildServerConnection.RunServerBuildRequestAsync( request, pipeName, timeoutOverride: Timeout.Infinite, tryCreateServerFunc: (_, _) => false, logger, cancellationToken); var compileFunc = GetCompileFunc(language); return new BuildClient(logger, language, compileFunc, compileOnServerFunc); } internal static CompileFunc GetCompileFunc(RequestLanguage language) { Func<string[], string, string, string, string, TextWriter, IAnalyzerAssemblyLoader, int> func; switch (language) { case RequestLanguage.CSharpCompile: func = csc.Microsoft.CodeAnalysis.CSharp.CommandLine.Program.Run; break; case RequestLanguage.VisualBasicCompile: func = vbc.Microsoft.CodeAnalysis.VisualBasic.CommandLine.Program.Run; break; default: throw new InvalidOperationException(); } return (args, buildPaths, textWriter, loader) => func(args, buildPaths.ClientDirectory, buildPaths.WorkingDirectory, buildPaths.SdkDirectory, buildPaths.TempDirectory, textWriter, loader); } } }