| File: Hosting\TestHostManagerCallbacks.cs | Web Access |
| Project: src\vstest\src\Microsoft.TestPlatform.TestHostProvider\Microsoft.TestPlatform.TestHostProvider.csproj (Microsoft.TestPlatform.TestHostRuntimeProvider) |
// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Text; using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Extensions; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Host; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces; namespace Microsoft.TestPlatform.TestHostProvider.Hosting; internal class TestHostManagerCallbacks { private const int E_HANDLE = unchecked((int)0x80070006); private readonly bool _forwardOutput; private readonly IMessageLogger? _messageLogger; public TestHostManagerCallbacks(bool forwardOutput, IMessageLogger? logger) { if (forwardOutput) { string? but = null; if (logger == null) { but = " But logger is null, so it won't forward any output."; } EqtTrace.Verbose($"TestHostManagerCallbacks.ctor: Forwarding output is enabled.{but}"); } else { EqtTrace.Verbose($"TestHostManagerCallbacks.ctor: Forwarding output is disabled."); } _forwardOutput = forwardOutput; _messageLogger = logger; } public void StandardOutputReceivedCallback(StringBuilder testHostProcessStdOut, string? data) { EqtTrace.Verbose("TestHostManagerCallbacks.StandardOutputReceivedCallback Test host standard output line: {0}", data); testHostProcessStdOut.AppendSafeWithNewLine(data); if (_forwardOutput && _messageLogger != null && !StringUtils.IsNullOrWhiteSpace(data)) { _messageLogger.SendMessage(TestMessageLevel.Informational, data); } } public void ErrorReceivedCallback(StringBuilder testHostProcessStdError, string? data) { // Log all standard error message because on too much data we ignore starting part. // This is helpful in abnormal failure of testhost. EqtTrace.Warning("TestHostManagerCallbacks.ErrorReceivedCallback Test host standard error line: {0}", data); testHostProcessStdError.AppendSafeWithNewLine(data); if (_forwardOutput && _messageLogger != null && !StringUtils.IsNullOrWhiteSpace(data)) { // Forward the error output, but DO NOT forward it as error. Until now it was only written into logs, // and applications love to write Debug messages into error stream. Which we do not want to fail the test run. _messageLogger.SendMessage(TestMessageLevel.Informational, data); } } public static void ExitCallBack( IProcessHelper processHelper, object? process, StringBuilder testHostProcessStdError, Action<HostProviderEventArgs> onHostExited) { EqtTrace.Verbose("TestHostProvider.ExitCallBack: Host exited starting callback."); var testHostProcessStdErrorStr = testHostProcessStdError.ToString(); int exitCode; try { processHelper.TryGetExitCode(process, out exitCode); } catch (COMException ex) when (ex.HResult == E_HANDLE) { exitCode = -1; EqtTrace.Error("TestHostProvider.ExitCallBack: Invalid process handle we cannot get the error code, error {0}.", ex); } int procId = -1; try { if (process is Process p) { procId = p.Id; } } catch (InvalidOperationException ex) { EqtTrace.Error("TestHostProvider.ExitCallBack: could not get proccess id from process, error: {0}.", ex); } if (exitCode != 0) { EqtTrace.Error("TestHostManagerCallbacks.ExitCallBack: Testhost processId: {0} exited with exitcode: {1} error: '{2}'", procId, exitCode, testHostProcessStdErrorStr); } else { EqtTrace.Info("TestHostManagerCallbacks.ExitCallBack: Testhost processId: {0} exited with exitcode: 0 error: '{1}'", procId, testHostProcessStdErrorStr); } onHostExited(new HostProviderEventArgs(testHostProcessStdErrorStr, exitCode, procId)); } }