Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only push output device messages to Test Explorer, don't push logs #4178

Merged
merged 32 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2d96954
Only push output device messages to Test Explorer, don't push logs
Youssef1313 Nov 27, 2024
1231ea9
Remove unused usings
Youssef1313 Nov 27, 2024
a3429b0
Adjust test expectation for the new behavior
Youssef1313 Nov 27, 2024
3b0d18d
Review comments
Youssef1313 Nov 28, 2024
0a7df37
Cleanup possibly redundant check
Youssef1313 Nov 28, 2024
f7d05a5
Always log something in before session
Youssef1313 Nov 28, 2024
e3299b8
Adjust
Youssef1313 Nov 29, 2024
13766b5
Fix assert back and TODO
Youssef1313 Nov 29, 2024
c714c07
Add test
Youssef1313 Nov 29, 2024
968c79b
Localize
Youssef1313 Nov 29, 2024
6767387
Fix test
Youssef1313 Nov 30, 2024
74da123
Address review comment
Youssef1313 Dec 2, 2024
5560e40
Refactor
Youssef1313 Dec 2, 2024
7874d42
Further cleanup
Youssef1313 Dec 2, 2024
a823ac0
Fix build errors
Youssef1313 Dec 2, 2024
f6d0c1b
Fix missing initialization
Youssef1313 Dec 2, 2024
7e7fb18
Merge branch 'main' into logging-improvements
Youssef1313 Dec 3, 2024
edf7751
Follow-up per recent changes
Youssef1313 Dec 3, 2024
b4bf543
Remove unused using
Youssef1313 Dec 3, 2024
1d355c2
Temporary restore `FormattedTextOutputDeviceDataBuilder`
Youssef1313 Dec 3, 2024
25dbaaf
Adjust
Youssef1313 Dec 3, 2024
adb40be
Progress
Youssef1313 Dec 3, 2024
7b778db
Merge branch 'main' into logging-improvements
Youssef1313 Dec 3, 2024
d7f4353
Merge branch 'logging-improvements' of https://github.com/Youssef1313…
Youssef1313 Dec 3, 2024
074227f
Forward some messages to output device (hence, to TE)
Youssef1313 Dec 3, 2024
870fb58
Cleanup
Youssef1313 Dec 3, 2024
09aad35
Remove unused using
Youssef1313 Dec 3, 2024
c2edd6a
Decouple IOutputDevice from IPlatformOutputDevice
Youssef1313 Dec 3, 2024
59fd67a
Avoid async void
Youssef1313 Dec 3, 2024
26964af
Fix race
Youssef1313 Dec 4, 2024
39971bd
Address review comments
Youssef1313 Dec 4, 2024
e3e2907
Move comment to server mode output device
Youssef1313 Dec 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ internal interface ICommandLineHandler
{
bool IsHelpInvoked();

Task PrintHelpAsync(IPlatformOutputDevice platformOutputDevice, IReadOnlyList<ITool>? availableTools = null);
Task PrintHelpAsync(IOutputDevice outputDevice, IReadOnlyList<ITool>? availableTools = null);
}
16 changes: 10 additions & 6 deletions src/Platform/Microsoft.Testing.Platform/Hosts/CommonTestHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,11 @@ public async Task<int> RunAsync()
await DisposeServiceProviderAsync(ServiceProvider, isProcessShutdown: true);
await DisposeHelper.DisposeAsync(ServiceProvider.GetService<FileLoggerProvider>());
await DisposeHelper.DisposeAsync(PushOnlyProtocol);
await DisposeHelper.DisposeAsync(ServiceProvider.GetTestApplicationCancellationTokenSource());

// This is intentional that we are not disposing the CTS.
// An unobserved task exception could be raised after the dispose, and we want to use OutputDevice there
// which needs CTS down the path.
// await DisposeHelper.DisposeAsync(ServiceProvider.GetTestApplicationCancellationTokenSource());
}

if (testApplicationCancellationToken.IsCancellationRequested)
Expand Down Expand Up @@ -119,7 +123,7 @@ private async Task<int> RunTestAppAsync(CancellationToken testApplicationCancell

protected abstract Task<int> InternalRunAsync();

protected static async Task ExecuteRequestAsync(IPlatformOutputDevice outputDevice, ITestSessionContext testSessionInfo,
protected static async Task ExecuteRequestAsync(ProxyOutputDevice outputDevice, ITestSessionContext testSessionInfo,
ServiceProvider serviceProvider, BaseMessageBus baseMessageBus, ITestFramework testFramework, TestHost.ClientInfo client)
{
CancellationToken testSessionCancellationToken = serviceProvider.GetTestSessionContext().CancellationToken;
Expand All @@ -142,26 +146,26 @@ protected static async Task ExecuteRequestAsync(IPlatformOutputDevice outputDevi
await DisplayAfterSessionEndRunAsync(outputDevice, testSessionInfo, testSessionCancellationToken);
}

private static async Task DisplayBeforeSessionStartAsync(IPlatformOutputDevice outputDevice, ITestSessionContext sessionInfo, CancellationToken cancellationToken)
private static async Task DisplayBeforeSessionStartAsync(ProxyOutputDevice outputDevice, ITestSessionContext sessionInfo, CancellationToken cancellationToken)
{
// Display before session start
await outputDevice.DisplayBeforeSessionStartAsync();

if (outputDevice is ITestSessionLifetimeHandler testSessionLifetimeHandler)
if (outputDevice.OriginalOutputDevice is ITestSessionLifetimeHandler testSessionLifetimeHandler)
{
await testSessionLifetimeHandler.OnTestSessionStartingAsync(
sessionInfo.SessionId,
cancellationToken);
}
}

private static async Task DisplayAfterSessionEndRunAsync(IPlatformOutputDevice outputDevice, ITestSessionContext sessionInfo, CancellationToken cancellationToken)
private static async Task DisplayAfterSessionEndRunAsync(ProxyOutputDevice outputDevice, ITestSessionContext sessionInfo, CancellationToken cancellationToken)
{
// Display after session end
await outputDevice.DisplayAfterSessionEndRunAsync();

// We want to ensure that the output service is the last one to run
if (outputDevice is ITestSessionLifetimeHandler testSessionLifetimeHandlerFinishing)
if (outputDevice.OriginalOutputDevice is ITestSessionLifetimeHandler testSessionLifetimeHandlerFinishing)
{
await testSessionLifetimeHandlerFinishing.OnTestSessionFinishingAsync(
sessionInfo.SessionId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,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.Requests;
using Microsoft.Testing.Platform.Services;
using Microsoft.Testing.Platform.Telemetry;
Expand Down Expand Up @@ -83,7 +84,7 @@ protected override async Task<int> InternalRunAsync()
ITestSessionContext testSessionInfo = ServiceProvider.GetTestSessionContext();

await ExecuteRequestAsync(
ServiceProvider.GetPlatformOutputDevice(),
(ProxyOutputDevice)ServiceProvider.GetOutputDevice(),
testSessionInfo,
ServiceProvider,
ServiceProvider.GetBaseMessageBus(),
Expand Down
61 changes: 49 additions & 12 deletions src/Platform/Microsoft.Testing.Platform/Hosts/ServerTestHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,28 @@

using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Net.Sockets;

using Microsoft.Testing.Internal.Framework;
using Microsoft.Testing.Platform.Capabilities.TestFramework;
using Microsoft.Testing.Platform.Extensions.Messages;
using Microsoft.Testing.Platform.Extensions.OutputDevice;
using Microsoft.Testing.Platform.Extensions.TestFramework;
using Microsoft.Testing.Platform.Helpers;
using Microsoft.Testing.Platform.Logging;
using Microsoft.Testing.Platform.Messages;
using Microsoft.Testing.Platform.OutputDevice;
using Microsoft.Testing.Platform.Requests;
using Microsoft.Testing.Platform.Resources;
using Microsoft.Testing.Platform.ServerMode;
using Microsoft.Testing.Platform.Services;
using Microsoft.Testing.Platform.Telemetry;
using Microsoft.Testing.Platform.TestHost;

namespace Microsoft.Testing.Platform.Hosts;

internal sealed partial class ServerTestHost : CommonTestHost, IServerTestHost, IDisposable
internal sealed partial class ServerTestHost : CommonTestHost, IServerTestHost, IDisposable, IOutputDeviceDataProducer
{
public const string ProtocolVersion = "1.0.0";
private readonly Func<TestFrameworkBuilderData, Task<ITestFramework>> _buildTestFrameworkAsync;
Expand Down Expand Up @@ -93,13 +97,36 @@ public ServerTestHost(

protected override bool RunTestApplicationLifeCycleCallbacks => true;

public string Uid => nameof(ServerTestHost);

public string Version => AppVersion.DefaultSemVer;

public string DisplayName => PlatformResources.ServerTestHostDisplayName;

public string Description => PlatformResources.ServerTestHostDescription;

private void OnCurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
=> _logger.LogWarning($"[ServerTestHost.OnCurrentDomainUnhandledException] {e.ExceptionObject}{_environment.NewLine}IsTerminating: {e.IsTerminating}");
{
_logger.LogWarning($"[ServerTestHost.OnCurrentDomainUnhandledException] {e.ExceptionObject}{_environment.NewLine}IsTerminating: {e.IsTerminating}");

// Looks like nothing in this message to really be localized?
// All are class names, method names, property names, and placeholders. So none is localizable?
ServiceProvider.GetOutputDevice().DisplayAsync(
this,
new WarningMessageOutputDeviceData(
$"[ServerTestHost.OnCurrentDomainUnhandledException] {e.ExceptionObject}{_environment.NewLine}IsTerminating: {e.IsTerminating}"))
.GetAwaiter().GetResult();
Evangelink marked this conversation as resolved.
Show resolved Hide resolved
}

private void OnTaskSchedulerUnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e)
{
e.SetObserved();
_logger.LogWarning($"[ServerTestHost.OnTaskSchedulerUnobservedTaskException] Unhandled exception: {e.Exception}");

// Looks like nothing in this message to really be localized?
// All are class names, method names, property names, and placeholders. So none is localizable?
ServiceProvider.GetOutputDevice().DisplayAsync(this, new WarningMessageOutputDeviceData(PlatformResources.UnobservedTaskExceptionWarningMessage))
.GetAwaiter().GetResult();
}

[MemberNotNull(nameof(_messageHandler))]
Expand All @@ -118,13 +145,6 @@ protected override async Task<int> InternalRunAsync()
await _logger.LogDebugAsync("Starting server mode");
_messageHandler = await _messageHandlerFactory.CreateMessageHandlerAsync(_testApplicationCancellationTokenSource.CancellationToken);

// Initialize the ServerLoggerForwarderProvider, it can be null if diagnostic is disabled.
ServerLoggerForwarderProvider? serviceLoggerForwarder = ServiceProvider.GetService<ServerLoggerForwarderProvider>();
if (serviceLoggerForwarder is not null)
{
await serviceLoggerForwarder.InitializeAsync(this);
}

await HandleMessagesAsync();

(_messageHandler as IDisposable)?.Dispose();
Expand Down Expand Up @@ -268,7 +288,14 @@ private async Task HandleNotificationAsync(NotificationMessage message, Cancella
Exception? cancellationException = rpcState.CancelRequest();
if (cancellationException is null)
{
// This is intentionally not using PlatformResources.ExceptionDuringCancellationWarningMessage
// It's meant for troubleshooting and shouldn't be localized.
// The localized message that is user-facing will be displayed in the DisplayAsync call next line.
await _logger.LogWarningAsync($"Exception during the cancellation of request id '{args.CancelRequestId}'");

await ServiceProvider.GetOutputDevice().DisplayAsync(
this,
new WarningMessageOutputDeviceData(string.Format(CultureInfo.InvariantCulture, PlatformResources.ExceptionDuringCancellationWarningMessage, args.CancelRequestId)));
}
}

Expand Down Expand Up @@ -450,13 +477,16 @@ private async Task<ResponseArgsBase> ExecuteRequestAsync(RequestArgsBase args, s

DateTimeOffset adapterLoadStart = _clock.UtcNow;

ProxyOutputDevice outputDevice = ServiceProvider.GetRequiredService<ProxyOutputDevice>();
await outputDevice.InitializeAsync(this);

// Build the per request adapter
ITestFramework perRequestTestFramework = await _buildTestFrameworkAsync(new(
ITestFramework perRequestTestFramework = await _buildTestFrameworkAsync(new TestFrameworkBuilderData(
perRequestServiceProvider,
requestFactory,
invoker,
filterFactory,
new ServerModePerCallOutputDevice(),
outputDevice.OriginalOutputDevice,
[testNodeUpdateProcessor],
_testFrameworkManager,
_testSessionManager,
Expand All @@ -475,7 +505,7 @@ private async Task<ResponseArgsBase> ExecuteRequestAsync(RequestArgsBase args, s

// Execute the request
await ExecuteRequestAsync(
perRequestServiceProvider.GetPlatformOutputDevice(),
outputDevice,
perRequestServiceProvider.GetTestSessionContext(),
perRequestServiceProvider,
perRequestServiceProvider.GetBaseMessageBus(),
Expand Down Expand Up @@ -632,6 +662,11 @@ public void Dispose()
{
// Note: The lifetime of the _reader/_writer should be currently handled by the RunAsync()
// We could consider creating a stateful engine that has the lifetime == server connection UP.
if (!ServiceProvider.GetUnhandledExceptionsPolicy().FastFailOnFailure)
{
AppDomain.CurrentDomain.UnhandledException -= OnCurrentDomainUnhandledException;
TaskScheduler.UnobservedTaskException -= OnTaskSchedulerUnobservedTaskException;
}
}

internal async Task SendTestUpdateCompleteAsync(Guid runId)
Expand Down Expand Up @@ -700,6 +735,8 @@ await SendMessageAsync(
}
}

public Task<bool> IsEnabledAsync() => throw new NotImplementedException();

private sealed class RpcInvocationState : IDisposable
{
private readonly Lock _cancellationTokenSourceLock = new();
Expand Down
Loading