Prepare for multi push-only protocol (#3779)

This commit is contained in:
Marco Rossignoli 2024-09-06 18:16:58 +02:00 коммит произвёл GitHub
Родитель 54841340f9
Коммит 2d649f59dc
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
24 изменённых файлов: 89 добавлений и 51 удалений

Просмотреть файл

@ -2,6 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.Testing.Platform.Hosts;
using Microsoft.Testing.Platform.ServerMode;
using Microsoft.Testing.Platform.Services;
namespace Microsoft.Testing.Platform.CommandLine;
@ -15,18 +16,18 @@ internal sealed class InformativeCommandLineTestHost(int returnValue, IServicePr
{
private readonly int _returnValue = returnValue;
private DotnetTestConnection? DotnetTestConnection => serviceProvider.GetService<DotnetTestConnection>();
private IPushOnlyProtocol? PushOnlyProtocol => serviceProvider.GetService<IPushOnlyProtocol>();
public Task<int> RunAsync() => Task.FromResult(_returnValue);
public void Dispose() => DotnetTestConnection?.Dispose();
public void Dispose() => PushOnlyProtocol?.Dispose();
#if NETCOREAPP
public async ValueTask DisposeAsync()
{
if (DotnetTestConnection is not null)
if (PushOnlyProtocol is not null)
{
await DotnetTestConnection.DisposeAsync();
await PushOnlyProtocol.DisposeAsync();
}
}
#endif

Просмотреть файл

@ -7,6 +7,7 @@ using Microsoft.Testing.Platform.Helpers;
using Microsoft.Testing.Platform.Logging;
using Microsoft.Testing.Platform.Messages;
using Microsoft.Testing.Platform.OutputDevice;
using Microsoft.Testing.Platform.ServerMode;
using Microsoft.Testing.Platform.Services;
using Microsoft.Testing.Platform.Telemetry;
using Microsoft.Testing.Platform.TestHost;
@ -17,7 +18,7 @@ internal abstract class CommonTestHost(ServiceProvider serviceProvider) : ITestH
{
public ServiceProvider ServiceProvider => serviceProvider;
protected DotnetTestConnection? DotnetTestConnection => ServiceProvider.GetService<DotnetTestConnection>();
protected IPushOnlyProtocol? PushOnlyProtocol => ServiceProvider.GetService<IPushOnlyProtocol>();
protected abstract bool RunTestApplicationLifeCycleCallbacks { get; }
@ -28,18 +29,18 @@ internal abstract class CommonTestHost(ServiceProvider serviceProvider) : ITestH
int exitCode;
try
{
if (DotnetTestConnection is null || DotnetTestConnection?.IsConnected == false)
if (PushOnlyProtocol is null || PushOnlyProtocol?.IsServerMode == false)
{
exitCode = await RunTestAppAsync(testApplicationCancellationToken);
return exitCode;
}
RoslynDebug.Assert(DotnetTestConnection is not null);
RoslynDebug.Assert(PushOnlyProtocol is not null);
ITestApplicationModuleInfo testApplicationModuleInfo = serviceProvider.GetTestApplicationModuleInfo();
bool isDotnetTestHandshakeSuccessful = await DotnetTestConnection.DoHandshakeAsync(GetHostType());
bool isValidProtocol = await PushOnlyProtocol.IsCompatibleProtocolAsync(GetHostType());
exitCode = isDotnetTestHandshakeSuccessful
exitCode = isValidProtocol
? await RunTestAppAsync(testApplicationCancellationToken)
: ExitCodes.IncompatibleProtocolVersion;
}
@ -52,7 +53,7 @@ internal abstract class CommonTestHost(ServiceProvider serviceProvider) : ITestH
{
await DisposeServiceProviderAsync(ServiceProvider, isProcessShutdown: true);
await DisposeHelper.DisposeAsync(ServiceProvider.GetService<FileLoggerProvider>());
await DisposeHelper.DisposeAsync(DotnetTestConnection);
await DisposeHelper.DisposeAsync(PushOnlyProtocol);
await DisposeHelper.DisposeAsync(ServiceProvider.GetTestApplicationCancellationTokenSource());
}
@ -99,7 +100,7 @@ internal abstract class CommonTestHost(ServiceProvider serviceProvider) : ITestH
protected abstract Task<int> InternalRunAsync();
protected static async Task ExecuteRequestAsync(IPlatformOutputDevice outputDevice, ITestSessionContext testSessionInfo,
ServiceProvider serviceProvider, BaseMessageBus baseMessageBus, ITestFramework testFramework, ClientInfo client)
ServiceProvider serviceProvider, BaseMessageBus baseMessageBus, ITestFramework testFramework, TestHost.ClientInfo client)
{
CancellationToken testSessionCancellationToken = serviceProvider.GetTestSessionContext().CancellationToken;

Просмотреть файл

@ -349,19 +349,19 @@ internal class TestHostBuilder(IFileSystem fileSystem, IRuntimeFeature runtimeFe
return toolsTestHost;
}
DotnetTestConnection dotnetTestConnection = new(commandLineHandler, processHandler, environment, _testApplicationModuleInfo, testApplicationCancellationTokenSource);
bool isConnectedToDotnetTest = await dotnetTestConnection.TryConnectToDotnetTestPipeIfAvailableAsync();
if (isConnectedToDotnetTest)
var pushOnlyProtocol = new DotnetTestConnection(commandLineHandler, processHandler, environment, _testApplicationModuleInfo, testApplicationCancellationTokenSource);
await pushOnlyProtocol.AfterCommonServiceSetupAsync();
if (pushOnlyProtocol.IsServerMode)
{
serviceProvider.AddService(dotnetTestConnection);
serviceProvider.AddService(pushOnlyProtocol);
}
// If --help is invoked we return
if (commandLineHandler.IsHelpInvoked())
{
if (isConnectedToDotnetTest)
if (pushOnlyProtocol.IsServerMode)
{
await dotnetTestConnection.SendCommandLineOptionsToDotnetTestPipeAsync();
await pushOnlyProtocol.HelpInvokedAsync();
}
else
{
@ -625,12 +625,12 @@ internal class TestHostBuilder(IFileSystem fileSystem, IRuntimeFeature runtimeFe
serviceProvider.AddService(testFrameworkBuilderData.MessageBusProxy);
// Check if we're connected to the dotnet test pipe
DotnetTestDataConsumer? dotnetTestDataConsumer = null;
DotnetTestConnection? dotnetTestConnection = serviceProvider.GetService<DotnetTestConnection>();
IPushOnlyProtocolConsumer? pushOnlyProtocolDataConsumer = null;
IPushOnlyProtocol? pushOnlyProtocol = serviceProvider.GetService<IPushOnlyProtocol>();
if (dotnetTestConnection?.IsConnected == true)
if (pushOnlyProtocol?.IsServerMode == true)
{
dotnetTestDataConsumer = new DotnetTestDataConsumer(dotnetTestConnection, serviceProvider.GetEnvironment());
pushOnlyProtocolDataConsumer = await pushOnlyProtocol.GetDataConsumerAsync();
}
// Build and register "common non special" services - we need special treatment because extensions can start to log during the
@ -703,9 +703,9 @@ internal class TestHostBuilder(IFileSystem fileSystem, IRuntimeFeature runtimeFe
// Register the test session lifetime handlers container
// We register the lifetime handler if we're connected to the dotnet test pipe
if (dotnetTestDataConsumer is not null)
if (pushOnlyProtocolDataConsumer is not null)
{
testSessionLifetimeHandlers.Add(dotnetTestDataConsumer);
testSessionLifetimeHandlers.Add(pushOnlyProtocolDataConsumer);
}
TestSessionLifetimeHandlersContainer testSessionLifetimeHandlersContainer = new(testSessionLifetimeHandlers);
@ -720,9 +720,9 @@ internal class TestHostBuilder(IFileSystem fileSystem, IRuntimeFeature runtimeFe
await RegisterAsServiceOrConsumerOrBothAsync(testApplicationResult, serviceProvider, dataConsumersBuilder);
// We register the data consumer handler if we're connected to the dotnet test pipe
if (dotnetTestDataConsumer is not null)
if (pushOnlyProtocolDataConsumer is not null)
{
dataConsumersBuilder.Add(dotnetTestDataConsumer);
dataConsumersBuilder.Add(pushOnlyProtocolDataConsumer);
}
IDataConsumer[] dataConsumerServices = dataConsumersBuilder.ToArray();
@ -740,8 +740,8 @@ internal class TestHostBuilder(IFileSystem fileSystem, IRuntimeFeature runtimeFe
serviceProvider.GetAsyncMonitorFactory(),
serviceProvider.GetEnvironment(),
serviceProvider.GetTestApplicationProcessExitCode(),
dotnetTestConnection,
dotnetTestDataConsumer);
pushOnlyProtocol,
pushOnlyProtocolDataConsumer);
await concreteMessageBusService.InitAsync();
testFrameworkBuilderData.MessageBusProxy.SetBuiltMessageBus(concreteMessageBusService);
}

Просмотреть файл

@ -19,6 +19,7 @@ using Microsoft.Testing.Platform.Logging;
using Microsoft.Testing.Platform.Messages;
using Microsoft.Testing.Platform.OutputDevice;
using Microsoft.Testing.Platform.Resources;
using Microsoft.Testing.Platform.ServerMode;
using Microsoft.Testing.Platform.Services;
using Microsoft.Testing.Platform.Telemetry;
using Microsoft.Testing.Platform.TestHostControllers;
@ -129,13 +130,13 @@ internal sealed class TestHostControllersTestHost : CommonTestHost, ITestHost, I
dataConsumersBuilder.Add(dataConsumerDisplay);
}
DotnetTestConnection? dotnetTestConnection = ServiceProvider.GetService<DotnetTestConnection>();
IPushOnlyProtocol? pushOnlyProtocol = ServiceProvider.GetService<IPushOnlyProtocol>();
// We register the DotnetTestDataConsumer as last to ensure that it will be the last one to consume the data.
if (DotnetTestConnection?.IsConnected == true)
if (pushOnlyProtocol?.IsServerMode == true)
{
RoslynDebug.Assert(dotnetTestConnection is not null);
RoslynDebug.Assert(pushOnlyProtocol is not null);
dataConsumersBuilder.Add(new DotnetTestDataConsumer(dotnetTestConnection, ServiceProvider.GetEnvironment()));
dataConsumersBuilder.Add(await pushOnlyProtocol.GetDataConsumerAsync());
}
AsynchronousMessageBus concreteMessageBusService = new(

Просмотреть файл

@ -6,9 +6,9 @@ using Microsoft.Testing.Platform.Extensions.OutputDevice;
using Microsoft.Testing.Platform.Extensions.TestFramework;
using Microsoft.Testing.Platform.Extensions.TestHost;
using Microsoft.Testing.Platform.Helpers;
using Microsoft.Testing.Platform.IPC;
using Microsoft.Testing.Platform.Logging;
using Microsoft.Testing.Platform.OutputDevice;
using Microsoft.Testing.Platform.ServerMode;
using Microsoft.Testing.Platform.Services;
namespace Microsoft.Testing.Platform.Messages;
@ -21,16 +21,16 @@ internal sealed class ListTestsMessageBus(
IAsyncMonitorFactory asyncMonitorFactory,
IEnvironment environment,
ITestApplicationProcessExitCode testApplicationProcessExitCode,
DotnetTestConnection? dotnetTestConnection,
DotnetTestDataConsumer? dotnetTestDataConsumer) : BaseMessageBus, IMessageBus, IDisposable, IOutputDeviceDataProducer
IPushOnlyProtocol? pushOnlyProtocol,
IPushOnlyProtocolConsumer? pushOnlyProtocolConsumer) : BaseMessageBus, IMessageBus, IDisposable, IOutputDeviceDataProducer
{
private readonly ITestFramework _testFramework = testFramework;
private readonly ITestApplicationCancellationTokenSource _testApplicationCancellationTokenSource = testApplicationCancellationTokenSource;
private readonly IOutputDevice _outputDisplay = outputDisplay;
private readonly IEnvironment _environment = environment;
private readonly ITestApplicationProcessExitCode _testApplicationProcessExitCode = testApplicationProcessExitCode;
private readonly DotnetTestConnection? _dotnetTestConnection = dotnetTestConnection;
private readonly DotnetTestDataConsumer? _dotnetTestDataConsumer = dotnetTestDataConsumer;
private readonly IPushOnlyProtocol? _pushOnlyProtocol = pushOnlyProtocol;
private readonly IPushOnlyProtocolConsumer? _pushOnlyProtocolConsumer = pushOnlyProtocolConsumer;
private readonly ILogger<ListTestsMessageBus> _logger = loggerFactory.CreateLogger<ListTestsMessageBus>();
private readonly IAsyncMonitor _asyncMonitor = asyncMonitorFactory.Create();
private bool _printTitle = true;
@ -75,11 +75,11 @@ internal sealed class ListTestsMessageBus(
return;
}
if (_dotnetTestConnection?.IsConnected == true)
if (_pushOnlyProtocol?.IsServerMode == true)
{
RoslynDebug.Assert(_dotnetTestDataConsumer is not null);
RoslynDebug.Assert(_pushOnlyProtocolConsumer is not null);
await _dotnetTestDataConsumer.ConsumeAsync(dataProducer, data, _testApplicationCancellationTokenSource.CancellationToken);
await _pushOnlyProtocolConsumer.ConsumeAsync(dataProducer, data, _testApplicationCancellationTokenSource.CancellationToken);
}
// Send the information to the ITestApplicationProcessExitCode to correctly handle the ZeroTest case.

Просмотреть файл

@ -10,12 +10,13 @@ using Microsoft.Testing.Platform.Helpers;
using Microsoft.Testing.Platform.IPC;
using Microsoft.Testing.Platform.IPC.Models;
using Microsoft.Testing.Platform.IPC.Serializers;
using Microsoft.Testing.Platform.ServerMode;
using Microsoft.Testing.Platform.Services;
using Microsoft.Testing.Platform.Tools;
namespace Microsoft.Testing.Platform;
internal sealed class DotnetTestConnection :
internal sealed class DotnetTestConnection : IPushOnlyProtocol,
#if NETCOREAPP
IAsyncDisposable,
#endif
@ -38,7 +39,14 @@ internal sealed class DotnetTestConnection :
_cancellationTokenSource = cancellationTokenSource;
}
public async Task<bool> TryConnectToDotnetTestPipeIfAvailableAsync()
public bool IsServerMode => _dotnetTestPipeClient?.IsConnected == true;
public string Name => PlatformCommandLineProvider.DotnetTestCliProtocolName;
public Task<IPushOnlyProtocolConsumer> GetDataConsumerAsync()
=> Task.FromResult((IPushOnlyProtocolConsumer)new DotnetTestDataConsumer(this, _environment));
public async Task AfterCommonServiceSetupAsync()
{
// If we are in server mode and the pipe name is provided
// then, we need to connect to the pipe server.
@ -57,14 +65,10 @@ internal sealed class DotnetTestConnection :
_dotnetTestPipeClient.RegisterAllSerializers();
await _dotnetTestPipeClient.ConnectAsync(_cancellationTokenSource.CancellationToken);
return _dotnetTestPipeClient.IsConnected;
}
return false;
}
public async Task SendCommandLineOptionsToDotnetTestPipeAsync()
public async Task HelpInvokedAsync()
{
RoslynDebug.Assert(_dotnetTestPipeClient is not null);
@ -89,7 +93,7 @@ internal sealed class DotnetTestConnection :
await _dotnetTestPipeClient.RequestReplyAsync<CommandLineOptionMessages, VoidResponse>(new CommandLineOptionMessages(_testApplicationModuleInfo.GetCurrentTestApplicationFullPath(), commandLineHelpOptions.OrderBy(option => option.Name).ToArray()), _cancellationTokenSource.CancellationToken);
}
public async Task<bool> DoHandshakeAsync(string hostType)
public async Task<bool> IsCompatibleProtocolAsync(string hostType)
{
RoslynDebug.Assert(_dotnetTestPipeClient is not null);
@ -138,8 +142,6 @@ internal sealed class DotnetTestConnection :
}
}
public bool IsConnected => _dotnetTestPipeClient?.IsConnected ?? false;
public void Dispose() => _dotnetTestPipeClient?.Dispose();
#if NETCOREAPP

Просмотреть файл

@ -2,14 +2,14 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.Testing.Platform.Extensions.Messages;
using Microsoft.Testing.Platform.Extensions.TestHost;
using Microsoft.Testing.Platform.Helpers;
using Microsoft.Testing.Platform.IPC.Models;
using Microsoft.Testing.Platform.ServerMode;
using Microsoft.Testing.Platform.TestHost;
namespace Microsoft.Testing.Platform.IPC;
internal class DotnetTestDataConsumer : IDataConsumer, ITestSessionLifetimeHandler
internal class DotnetTestDataConsumer : IPushOnlyProtocolConsumer
{
private readonly DotnetTestConnection? _dotnetTestConnection;
private readonly IEnvironment _environment;

Просмотреть файл

@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.Testing.Platform.ServerMode;
internal interface IPushOnlyProtocol :
#if NETCOREAPP
IAsyncDisposable,
#endif
IDisposable
{
string Name { get; }
bool IsServerMode { get; }
Task AfterCommonServiceSetupAsync();
Task HelpInvokedAsync();
Task<bool> IsCompatibleProtocolAsync(string testHostType);
Task<IPushOnlyProtocolConsumer> GetDataConsumerAsync();
}

Просмотреть файл

@ -0,0 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.Testing.Platform.Extensions.TestHost;
namespace Microsoft.Testing.Platform.ServerMode;
internal interface IPushOnlyProtocolConsumer : IDataConsumer, ITestSessionLifetimeHandler
{
}