diff --git a/playground/TestPlatform.Playground/Environment.cs b/playground/TestPlatform.Playground/Environment.cs index c23cd0ea3c..d7ba86c869 100644 --- a/playground/TestPlatform.Playground/Environment.cs +++ b/playground/TestPlatform.Playground/Environment.cs @@ -14,7 +14,6 @@ internal class EnvironmentVariables ["VSTEST_RUNNER_DEBUG_ATTACHVS"] = "0", ["VSTEST_HOST_DEBUG_ATTACHVS"] = "0", ["VSTEST_DATACOLLECTOR_DEBUG_ATTACHVS"] = "0", - ["VSTEST_EXPERIMENTAL_FORWARD_OUTPUT_FEATURE"] = "0" }; } diff --git a/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeClient.cs b/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeClient.cs index 567fa79602..b3e59ba1c5 100644 --- a/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeClient.cs +++ b/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeClient.cs @@ -25,7 +25,7 @@ using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions; using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces; -using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces; +using Microsoft.VisualStudio.TestPlatform.Utilities; using CommunicationUtilitiesResources = Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Resources.Resources; @@ -43,9 +43,8 @@ public class DesignModeClient : IDesignModeClient private readonly ProtocolConfig _protocolConfig = Constants.DefaultProtocolConfig; private readonly IEnvironment _platformEnvironment; private readonly TestSessionMessageLogger _testSessionMessageLogger; + private readonly bool _forwardOutput; private readonly object _lockObject = new(); - private readonly bool _isForwardingOutput; - [SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Part of the public API.")] [SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "Part of the public API")] protected Action? onCustomTestHostLaunchAckReceived; @@ -57,7 +56,7 @@ public class DesignModeClient : IDesignModeClient /// Initializes a new instance of the class. /// public DesignModeClient() - : this(new SocketCommunicationManager(), JsonDataSerializer.Instance, new PlatformEnvironment(), new EnvironmentVariableHelper()) + : this(new SocketCommunicationManager(), JsonDataSerializer.Instance, new PlatformEnvironment()) { } @@ -73,14 +72,14 @@ public DesignModeClient() /// /// The platform Environment /// - internal DesignModeClient(ICommunicationManager communicationManager, IDataSerializer dataSerializer, IEnvironment platformEnvironment, IEnvironmentVariableHelper environmentVariableHelper) + internal DesignModeClient(ICommunicationManager communicationManager, IDataSerializer dataSerializer, IEnvironment platformEnvironment) { _communicationManager = communicationManager; _dataSerializer = dataSerializer; _platformEnvironment = platformEnvironment; _testSessionMessageLogger = TestSessionMessageLogger.Instance; + _forwardOutput = !FeatureFlag.Instance.IsSet(FeatureFlag.VSTEST_DISABLE_STANDARD_OUTPUT_FORWARDING); _testSessionMessageLogger.TestRunMessage += TestRunMessageHandler; - _isForwardingOutput = environmentVariableHelper.GetEnvironmentVariable("VSTEST_EXPERIMENTAL_FORWARD_OUTPUT_FEATURE") == "1"; } /// @@ -449,8 +448,15 @@ public void TestRunMessageHandler(object? sender, TestRunMessageEventArgs e) case TestMessageLevel.Informational: EqtTrace.Info(e.Message); - if (_isForwardingOutput || EqtTrace.IsInfoEnabled) + // When forwarding output we need to allow Info messages that originate from this process (or more precisely from the IMessageLogger that we + // are observing, but we don't have an easy way to tell apart "output" and non-output info messages. So when output forwarding is enabled + // we forward any informational message. This is okay, because in worst case we will send few more messages forward that are not output. + // And that is fine, because any adapter has access to SendMessage(MessageLevel.Informational,...) which we don't filter anywhere (it is passed + // as a raw message and forwarded to VS), so any adapter can spam the Tests output in VS as it wants. + if (_forwardOutput || EqtTrace.IsInfoEnabled) + { SendTestMessage(e.Level, e.Message); + } break; diff --git a/src/Microsoft.TestPlatform.Common/ExtensionDecorators/ExtensionDecoratorFactory.cs b/src/Microsoft.TestPlatform.Common/ExtensionDecorators/ExtensionDecoratorFactory.cs index 758cef6fc7..7a82106c77 100644 --- a/src/Microsoft.TestPlatform.Common/ExtensionDecorators/ExtensionDecoratorFactory.cs +++ b/src/Microsoft.TestPlatform.Common/ExtensionDecorators/ExtensionDecoratorFactory.cs @@ -16,7 +16,7 @@ public ExtensionDecoratorFactory(IFeatureFlag featureFlag) public ITestExecutor Decorate(ITestExecutor originalTestExecutor) { - return _featureFlag.IsSet(FeatureFlag.DISABLE_SERIALTESTRUN_DECORATOR) + return _featureFlag.IsSet(FeatureFlag.VSTEST_DISABLE_SERIALTESTRUN_DECORATOR) ? originalTestExecutor : new SerialTestRunDecorator(originalTestExecutor); } diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/JsonDataSerializer.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/JsonDataSerializer.cs index 8ba648411d..ce3fe4ba58 100644 --- a/src/Microsoft.TestPlatform.CommunicationUtilities/JsonDataSerializer.cs +++ b/src/Microsoft.TestPlatform.CommunicationUtilities/JsonDataSerializer.cs @@ -22,7 +22,7 @@ public class JsonDataSerializer : IDataSerializer { private static JsonDataSerializer? s_instance; - private static readonly bool DisableFastJson = FeatureFlag.Instance.IsSet(FeatureFlag.DISABLE_FASTER_JSON_SERIALIZATION); + private static readonly bool DisableFastJson = FeatureFlag.Instance.IsSet(FeatureFlag.VSTEST_DISABLE_FASTER_JSON_SERIALIZATION); private static readonly JsonSerializer PayloadSerializerV1; // payload serializer for version <= 1 private static readonly JsonSerializer PayloadSerializerV2; // payload serializer for version >= 2 diff --git a/src/Microsoft.TestPlatform.CoreUtilities/FeatureFlag/FeatureFlag.cs b/src/Microsoft.TestPlatform.CoreUtilities/FeatureFlag/FeatureFlag.cs index 283d7d9a60..86a7f4c85b 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/FeatureFlag/FeatureFlag.cs +++ b/src/Microsoft.TestPlatform.CoreUtilities/FeatureFlag/FeatureFlag.cs @@ -32,33 +32,42 @@ private FeatureFlag() { } // Only check the env variable once, when it is not set or is set to 0, consider it unset. When it is anything else, consider it set. public bool IsSet(string featureFlag) => _cache.GetOrAdd(featureFlag, f => (Environment.GetEnvironmentVariable(f)?.Trim() ?? "0") != "0"); - private const string VSTEST_ = nameof(VSTEST_); - // Added for artifact post-processing, it enable/disable the post processing. // Added in 17.2-preview 7.0-preview - public const string DISABLE_ARTIFACTS_POSTPROCESSING = VSTEST_ + nameof(DISABLE_ARTIFACTS_POSTPROCESSING); + public const string VSTEST_DISABLE_ARTIFACTS_POSTPROCESSING = nameof(VSTEST_DISABLE_ARTIFACTS_POSTPROCESSING); // Added for artifact post-processing, it will show old output for dotnet sdk scenario. // It can be useful if we need to restore old UX in case users are parsing the console output. // Added in 17.2-preview 7.0-preview - public const string DISABLE_ARTIFACTS_POSTPROCESSING_NEW_SDK_UX = VSTEST_ + nameof(DISABLE_ARTIFACTS_POSTPROCESSING_NEW_SDK_UX); + public const string VSTEST_DISABLE_ARTIFACTS_POSTPROCESSING_NEW_SDK_UX = nameof(VSTEST_DISABLE_ARTIFACTS_POSTPROCESSING_NEW_SDK_UX); // Faster JSON serialization relies on less internals of NewtonsoftJson, and on some additional caching. - public const string DISABLE_FASTER_JSON_SERIALIZATION = VSTEST_ + nameof(DISABLE_FASTER_JSON_SERIALIZATION); + public const string VSTEST_DISABLE_FASTER_JSON_SERIALIZATION = nameof(VSTEST_DISABLE_FASTER_JSON_SERIALIZATION); // Forces vstest.console to run all sources using the same target framework (TFM) and architecture, instead of allowing // multiple different tfms and architectures to run at the same time. - public const string DISABLE_MULTI_TFM_RUN = VSTEST_ + nameof(DISABLE_MULTI_TFM_RUN); + public const string VSTEST_DISABLE_MULTI_TFM_RUN = nameof(VSTEST_DISABLE_MULTI_TFM_RUN); // Disables setting a higher value for SetMinThreads. Setting SetMinThreads value to higher allows testhost to connect back faster // even though we are blocking additional threads because we don't have to wait for ThreadPool to start more threads. - public const string DISABLE_THREADPOOL_SIZE_INCREASE = VSTEST_ + nameof(DISABLE_THREADPOOL_SIZE_INCREASE); + public const string VSTEST_DISABLE_THREADPOOL_SIZE_INCREASE = nameof(VSTEST_DISABLE_THREADPOOL_SIZE_INCREASE); // Disable the SerialTestRunDecorator - public const string DISABLE_SERIALTESTRUN_DECORATOR = VSTEST_ + nameof(DISABLE_SERIALTESTRUN_DECORATOR); + public const string VSTEST_DISABLE_SERIALTESTRUN_DECORATOR = nameof(VSTEST_DISABLE_SERIALTESTRUN_DECORATOR); // Disable setting UTF8 encoding in console. - public const string DISABLE_UTF8_CONSOLE_ENCODING = VSTEST_ + nameof(DISABLE_UTF8_CONSOLE_ENCODING); + public const string VSTEST_DISABLE_UTF8_CONSOLE_ENCODING = nameof(VSTEST_DISABLE_UTF8_CONSOLE_ENCODING); + + // VSTEST_EXPERIMENTAL_FORWARD_OUTPUT_FEATURE=1 replaced by the CAPTURING and FORWARDING flags, and was enabling + // the same behavior as what is now the default (both capture and forward set to TRUE). + // Because this is the new default we don't have to handle it in any special way. Setting it to 0 was not defined + // and so it also does not need any special treatment. + // + // Disable capturing standard output of testhost. + public const string VSTEST_DISABLE_STANDARD_OUTPUT_CAPTURING = nameof(VSTEST_DISABLE_STANDARD_OUTPUT_CAPTURING); + + // Disable forwarding standard output of testhost. + public const string VSTEST_DISABLE_STANDARD_OUTPUT_FORWARDING = nameof(VSTEST_DISABLE_STANDARD_OUTPUT_FORWARDING); [Obsolete("Only use this in tests.")] internal static void Reset() diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs index 485cb1630d..6290fce706 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs @@ -81,7 +81,7 @@ public void CollectArtifacts(TestRunCompleteEventArgs testRunCompleteEventArgs, ValidateArg.NotNull(testRunCompleteEventArgs, nameof(testRunCompleteEventArgs)); ValidateArg.NotNull(runSettingsXml, nameof(runSettingsXml)); - if (_featureFlag.IsSet(FeatureFlag.DISABLE_ARTIFACTS_POSTPROCESSING)) + if (_featureFlag.IsSet(FeatureFlag.VSTEST_DISABLE_ARTIFACTS_POSTPROCESSING)) { EqtTrace.Verbose("ArtifactProcessingManager.CollectArtifacts: Feature disabled"); return; @@ -120,7 +120,7 @@ public void CollectArtifacts(TestRunCompleteEventArgs testRunCompleteEventArgs, public async Task PostProcessArtifactsAsync() { - if (_featureFlag.IsSet(FeatureFlag.DISABLE_ARTIFACTS_POSTPROCESSING)) + if (_featureFlag.IsSet(FeatureFlag.VSTEST_DISABLE_ARTIFACTS_POSTPROCESSING)) { EqtTrace.Verbose("ArtifactProcessingManager.PostProcessArtifacts: Feature disabled"); return; diff --git a/src/Microsoft.TestPlatform.ObjectModel/PublicAPI/PublicAPI.Shipped.txt b/src/Microsoft.TestPlatform.ObjectModel/PublicAPI/PublicAPI.Shipped.txt index 803c09d86c..74364fb56a 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Microsoft.TestPlatform.ObjectModel/PublicAPI/PublicAPI.Shipped.txt @@ -972,3 +972,5 @@ virtual Microsoft.VisualStudio.TestPlatform.ObjectModel.TestObject.ProtectedGetP virtual Microsoft.VisualStudio.TestPlatform.ObjectModel.TestObject.ProtectedSetPropertyValue(Microsoft.VisualStudio.TestPlatform.ObjectModel.TestProperty! property, object? value) -> void virtual Microsoft.VisualStudio.TestPlatform.ObjectModel.ValidateValueCallback.Invoke(object? value) -> bool Microsoft.VisualStudio.TestPlatform.ObjectModel.Architecture.RiscV64 = 8 -> Microsoft.VisualStudio.TestPlatform.ObjectModel.Architecture +Microsoft.VisualStudio.TestPlatform.ObjectModel.RunConfiguration.CaptureStandardOutput.get -> bool +Microsoft.VisualStudio.TestPlatform.ObjectModel.RunConfiguration.ForwardStandardOutput.get -> bool diff --git a/src/Microsoft.TestPlatform.ObjectModel/RunSettings/RunConfiguration.cs b/src/Microsoft.TestPlatform.ObjectModel/RunSettings/RunConfiguration.cs index 12d88ce814..57f198202e 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/RunSettings/RunConfiguration.cs +++ b/src/Microsoft.TestPlatform.ObjectModel/RunSettings/RunConfiguration.cs @@ -9,6 +9,7 @@ using Microsoft.VisualStudio.TestPlatform.CoreUtilities; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions; +using Microsoft.VisualStudio.TestPlatform.Utilities; namespace Microsoft.VisualStudio.TestPlatform.ObjectModel; @@ -92,6 +93,8 @@ public RunConfiguration() : base(Constants.RunConfigurationSettingsName) _shouldCollectSourceInformation = false; TargetDevice = null; ExecutionThreadApartmentState = Constants.DefaultExecutionThreadApartmentState; + CaptureStandardOutput = !FeatureFlag.Instance.IsSet(FeatureFlag.VSTEST_DISABLE_STANDARD_OUTPUT_CAPTURING); + ForwardStandardOutput = !FeatureFlag.Instance.IsSet(FeatureFlag.VSTEST_DISABLE_STANDARD_OUTPUT_FORWARDING); } /// @@ -431,10 +434,27 @@ public bool ResultsDirectorySet /// public string? TestCaseFilter { get; private set; } + /// /// Path to dotnet executable to be used to invoke testhost.dll. Specifying this will skip looking up testhost.exe and will force usage of the testhost.dll. /// public string? DotnetHostPath { get; private set; } + /// + /// When true, we capture standard output of child processes. When false the standard output is not captured and it will end up in command line. + /// This makes the output visible to the user when running in vstest.console in-process. Such setup makes the behavior the same as in 17.6.3 and earlier. + /// + /// The recommended way is to use this with ForwardStandardOutput=true to forward output as informational messages so the output is always visible in console and VS, + /// unless the logging level is set to Warning or higher. + /// + /// Lastly this can be used with ForwardStandardOutput=false, to suppress the output in console, which is behavior of 17.7.0 till now. + /// + public bool CaptureStandardOutput { get; private set; } + + /// + /// Forward captured standard output of testhost as Informational test messages. Default is true. Needs CaptureStandardOutput to be true. + /// + public bool ForwardStandardOutput { get; private set; } + /// public override XmlElement ToXml() { @@ -550,6 +570,14 @@ public override XmlElement ToXml() root.AppendChild(treatAsError); } + XmlElement captureStandardOutput = doc.CreateElement(nameof(CaptureStandardOutput)); + captureStandardOutput.InnerXml = CaptureStandardOutput.ToString(); + root.AppendChild(captureStandardOutput); + + XmlElement forwardStandardOutput = doc.CreateElement(nameof(ForwardStandardOutput)); + forwardStandardOutput.InnerXml = ForwardStandardOutput.ToString(); + root.AppendChild(forwardStandardOutput); + return root; } @@ -907,6 +935,35 @@ public static RunConfiguration FromXml(XmlReader reader) case "TargetFrameworkTestHostDemultiplexer": reader.Skip(); break; + + case "CaptureStandardOutput": + XmlRunSettingsUtilities.ThrowOnHasAttributes(reader); + string captureStandardOutputStr = reader.ReadElementContentAsString(); + + bool bCaptureStandardOutput; + if (!bool.TryParse(captureStandardOutputStr, out bCaptureStandardOutput)) + { + throw new SettingsException(string.Format(CultureInfo.CurrentCulture, + Resources.Resources.InvalidSettingsIncorrectValue, Constants.RunConfigurationSettingsName, bCaptureStandardOutput, elementName)); + } + + runConfiguration.CaptureStandardOutput = bCaptureStandardOutput; + break; + + case "ForwardStandardOutput": + XmlRunSettingsUtilities.ThrowOnHasAttributes(reader); + string forwardStandardOutputStr = reader.ReadElementContentAsString(); + + bool bForwardStandardOutput; + if (!bool.TryParse(forwardStandardOutputStr, out bForwardStandardOutput)) + { + throw new SettingsException(string.Format(CultureInfo.CurrentCulture, + Resources.Resources.InvalidSettingsIncorrectValue, Constants.RunConfigurationSettingsName, bForwardStandardOutput, elementName)); + } + + runConfiguration.ForwardStandardOutput = bForwardStandardOutput; + break; + default: // Ignore a runsettings element that we don't understand. It could occur in the case // the test runner is of a newer version, but the test host is of an earlier version. diff --git a/src/Microsoft.TestPlatform.ObjectModel/TestProperty/TestProperty.cs b/src/Microsoft.TestPlatform.ObjectModel/TestProperty/TestProperty.cs index ed0e02b8be..911901b439 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/TestProperty/TestProperty.cs +++ b/src/Microsoft.TestPlatform.ObjectModel/TestProperty/TestProperty.cs @@ -19,7 +19,7 @@ public class TestProperty : IEquatable { private static readonly ConcurrentDictionary TypeCache = new(); - private static bool DisableFastJson { get; set; } = FeatureFlag.Instance.IsSet(FeatureFlag.DISABLE_FASTER_JSON_SERIALIZATION); + private static bool DisableFastJson { get; set; } = FeatureFlag.Instance.IsSet(FeatureFlag.VSTEST_DISABLE_FASTER_JSON_SERIALIZATION); private Type _valueType; diff --git a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DefaultTestHostManager.cs b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DefaultTestHostManager.cs index 7cd2abbace..ca4e20243e 100644 --- a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DefaultTestHostManager.cs +++ b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DefaultTestHostManager.cs @@ -66,6 +66,7 @@ public class DefaultTestHostManager : ITestRuntimeProvider2 private StringBuilder? _testHostProcessStdError; private StringBuilder? _testHostProcessStdOut; private IMessageLogger? _messageLogger; + private bool _captureOutput; private bool _hostExitedEventRaised; private TestHostManagerCallbacks? _testHostManagerCallbacks; @@ -371,7 +372,9 @@ public void Initialize(IMessageLogger? logger, string runsettingsXml) var runConfiguration = XmlRunSettingsUtilities.GetRunConfigurationNode(runsettingsXml); _messageLogger = logger; - _testHostManagerCallbacks = new TestHostManagerCallbacks(_environmentVariableHelper.GetEnvironmentVariable("VSTEST_EXPERIMENTAL_FORWARD_OUTPUT_FEATURE") == "1", logger); + _captureOutput = runConfiguration.CaptureStandardOutput; + var forwardOutput = runConfiguration.ForwardStandardOutput; + _testHostManagerCallbacks = new TestHostManagerCallbacks(forwardOutput, logger); _architecture = runConfiguration.TargetPlatform; _targetFramework = runConfiguration.TargetFramework; _testHostProcess = null; @@ -528,6 +531,7 @@ private bool LaunchHost(TestProcessStartInfo testHostStartInfo, CancellationToke { EqtTrace.Verbose("DefaultTestHostManager: Starting process '{0}' with command line '{1}'", testHostStartInfo.FileName, testHostStartInfo.Arguments); cancellationToken.ThrowIfCancellationRequested(); + var outputCallback = _captureOutput ? OutputReceivedCallback : null; _testHostProcess = _processHelper.LaunchProcess( testHostStartInfo.FileName!, testHostStartInfo.Arguments, @@ -535,7 +539,7 @@ private bool LaunchHost(TestProcessStartInfo testHostStartInfo, CancellationToke testHostStartInfo.EnvironmentVariables, ErrorReceivedCallback, ExitCallBack, - OutputReceivedCallback) as Process; + outputCallback) as Process; } else { diff --git a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs index e7bfe10491..b8e621f077 100644 --- a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs +++ b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs @@ -74,7 +74,7 @@ public class DotnetTestHostManager : ITestRuntimeProvider2 private Framework? _targetFramework; private bool _isVersionCheckRequired = true; private string? _dotnetHostPath; - + private bool _captureOutput; private protected TestHostManagerCallbacks? _testHostManagerCallbacks; /// @@ -190,9 +190,11 @@ private set public void Initialize(IMessageLogger? logger, string runsettingsXml) { _hostExitedEventRaised = false; - _testHostManagerCallbacks = new TestHostManagerCallbacks(_environmentVariableHelper.GetEnvironmentVariable("VSTEST_EXPERIMENTAL_FORWARD_OUTPUT_FEATURE") == "1", logger); - var runConfiguration = XmlRunSettingsUtilities.GetRunConfigurationNode(runsettingsXml); + _captureOutput = runConfiguration.CaptureStandardOutput; + var forwardOutput = runConfiguration.ForwardStandardOutput; + _testHostManagerCallbacks = new TestHostManagerCallbacks(forwardOutput, logger); + _architecture = runConfiguration.TargetPlatform; _targetFramework = runConfiguration.TargetFramework; _dotnetHostPath = runConfiguration.DotnetHostPath; @@ -738,6 +740,7 @@ private bool LaunchHost(TestProcessStartInfo testHostStartInfo, CancellationToke cancellationToken.ThrowIfCancellationRequested(); + var outputCallback = _captureOutput ? OutputReceivedCallback : null; _testHostProcess = _processHelper.LaunchProcess( testHostStartInfo.FileName!, testHostStartInfo.Arguments, @@ -745,7 +748,7 @@ private bool LaunchHost(TestProcessStartInfo testHostStartInfo, CancellationToke testHostStartInfo.EnvironmentVariables, ErrorReceivedCallback, ExitCallBack, - OutputReceivedCallback) as Process; + outputCallback) as Process; } else { diff --git a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/TestHostManagerCallbacks.cs b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/TestHostManagerCallbacks.cs index 6fabc016bc..9709607005 100644 --- a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/TestHostManagerCallbacks.cs +++ b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/TestHostManagerCallbacks.cs @@ -29,11 +29,11 @@ public TestHostManagerCallbacks(bool forwardOutput, IMessageLogger? logger) { but = " But logger is null, so it won't forward any output."; } - EqtTrace.Verbose($"TestHostManagerCallbacks.ctor: Experimental forwarding output is enabled.{but}"); + EqtTrace.Verbose($"TestHostManagerCallbacks.ctor: Forwarding output is enabled.{but}"); } else { - EqtTrace.Verbose($"TestHostManagerCallbacks.ctor: Experimental forwarding output is disabled."); + EqtTrace.Verbose($"TestHostManagerCallbacks.ctor: Forwarding output is disabled."); } _forwardOutput = forwardOutput; _messageLogger = logger; diff --git a/src/vstest.console/CommandLine/Executor.cs b/src/vstest.console/CommandLine/Executor.cs index 7de6f10111..beb8185b71 100644 --- a/src/vstest.console/CommandLine/Executor.cs +++ b/src/vstest.console/CommandLine/Executor.cs @@ -66,7 +66,7 @@ internal class Executor /// public Executor(IOutput output) : this(output, TestPlatformEventSource.Instance, new ProcessHelper(), new PlatformEnvironment()) { - if (!FeatureFlag.Instance.IsSet(FeatureFlag.DISABLE_THREADPOOL_SIZE_INCREASE)) + if (!FeatureFlag.Instance.IsSet(FeatureFlag.VSTEST_DISABLE_THREADPOOL_SIZE_INCREASE)) { // TODO: Get rid of this by making vstest.console code properly async. // The current implementation of vstest.console is blocking many threads that just wait diff --git a/src/vstest.console/Internal/ConsoleLogger.cs b/src/vstest.console/Internal/ConsoleLogger.cs index 7743e61a1d..5df4885ebc 100644 --- a/src/vstest.console/Internal/ConsoleLogger.cs +++ b/src/vstest.console/Internal/ConsoleLogger.cs @@ -680,9 +680,9 @@ private void TestRunCompleteHandler(object? sender, TestRunCompleteEventArgs e) if (runLevelAttachmentsCount > 0) { // If ARTIFACTS_POSTPROCESSING is disabled - if (_featureFlag.IsSet(FeatureFlag.DISABLE_ARTIFACTS_POSTPROCESSING) || + if (_featureFlag.IsSet(FeatureFlag.VSTEST_DISABLE_ARTIFACTS_POSTPROCESSING) || // DISABLE_ARTIFACTS_POSTPROCESSING_NEW_SDK_UX(new UX) is disabled - _featureFlag.IsSet(FeatureFlag.DISABLE_ARTIFACTS_POSTPROCESSING_NEW_SDK_UX) || + _featureFlag.IsSet(FeatureFlag.VSTEST_DISABLE_ARTIFACTS_POSTPROCESSING_NEW_SDK_UX) || // TestSessionCorrelationId is null(we're not running through the dotnet SDK). CommandLineOptions.Instance.TestSessionCorrelationId is null) { diff --git a/src/vstest.console/Processors/ArtifactProcessingPostProcessModeProcessor.cs b/src/vstest.console/Processors/ArtifactProcessingPostProcessModeProcessor.cs index c0aabbc3ef..0a446e1b61 100644 --- a/src/vstest.console/Processors/ArtifactProcessingPostProcessModeProcessor.cs +++ b/src/vstest.console/Processors/ArtifactProcessingPostProcessModeProcessor.cs @@ -45,7 +45,7 @@ public Lazy? Executor } public static bool ContainsPostProcessCommand(string[]? args, IFeatureFlag? featureFlag = null) - => !(featureFlag ?? FeatureFlag.Instance).IsSet(FeatureFlag.DISABLE_ARTIFACTS_POSTPROCESSING) && + => !(featureFlag ?? FeatureFlag.Instance).IsSet(FeatureFlag.VSTEST_DISABLE_ARTIFACTS_POSTPROCESSING) && (args?.Contains("--artifactsProcessingMode-postprocess", StringComparer.OrdinalIgnoreCase) == true || args?.Contains(CommandName, StringComparer.OrdinalIgnoreCase) == true); } diff --git a/src/vstest.console/Processors/Utilities/ArgumentProcessorFactory.cs b/src/vstest.console/Processors/Utilities/ArgumentProcessorFactory.cs index b8d001c66c..321df61125 100644 --- a/src/vstest.console/Processors/Utilities/ArgumentProcessorFactory.cs +++ b/src/vstest.console/Processors/Utilities/ArgumentProcessorFactory.cs @@ -51,7 +51,7 @@ internal static ArgumentProcessorFactory Create(IFeatureFlag? featureFlag = null { var defaultArgumentProcessor = DefaultArgumentProcessors; - if (!(featureFlag ?? FeatureFlag.Instance).IsSet(FeatureFlag.DISABLE_ARTIFACTS_POSTPROCESSING)) + if (!(featureFlag ?? FeatureFlag.Instance).IsSet(FeatureFlag.VSTEST_DISABLE_ARTIFACTS_POSTPROCESSING)) { defaultArgumentProcessor.Add(new ArtifactProcessingCollectModeProcessor()); defaultArgumentProcessor.Add(new ArtifactProcessingPostProcessModeProcessor()); diff --git a/src/vstest.console/Program.cs b/src/vstest.console/Program.cs index a44968c949..2ad1179585 100644 --- a/src/vstest.console/Program.cs +++ b/src/vstest.console/Program.cs @@ -23,7 +23,7 @@ public static class Program internal static int Run(string[]? args, UiLanguageOverride uiLanguageOverride) { - if (!FeatureFlag.Instance.IsSet(FeatureFlag.DISABLE_UTF8_CONSOLE_ENCODING)) + if (!FeatureFlag.Instance.IsSet(FeatureFlag.VSTEST_DISABLE_UTF8_CONSOLE_ENCODING)) { Console.OutputEncoding = Encoding.UTF8; } diff --git a/src/vstest.console/TestPlatformHelpers/TestRequestManager.cs b/src/vstest.console/TestPlatformHelpers/TestRequestManager.cs index 86e0727553..bd6c30de67 100644 --- a/src/vstest.console/TestPlatformHelpers/TestRequestManager.cs +++ b/src/vstest.console/TestPlatformHelpers/TestRequestManager.cs @@ -710,7 +710,7 @@ private bool UpdateRunSettingsIfRequired( // After MULTI_TFM sourceToArchitectureMap and sourceToFrameworkMap are the source of truth, and are propagated forward, // so when we want to revert to the older behavior we need to re-enable the check, and unify all the architecture and // framework entries to the same chosen value. - var disableMultiTfm = FeatureFlag.Instance.IsSet(FeatureFlag.DISABLE_MULTI_TFM_RUN); + var disableMultiTfm = FeatureFlag.Instance.IsSet(FeatureFlag.VSTEST_DISABLE_MULTI_TFM_RUN); // Choose default architecture based on the framework. // For a run with mixed tfms enabled, or .NET "Core", the default platform architecture should be based on the process. diff --git a/test/Microsoft.TestPlatform.Acceptance.IntegrationTests/DotnetTestTests.cs b/test/Microsoft.TestPlatform.Acceptance.IntegrationTests/DotnetTestTests.cs index 2b1cf303ad..0332671357 100644 --- a/test/Microsoft.TestPlatform.Acceptance.IntegrationTests/DotnetTestTests.cs +++ b/test/Microsoft.TestPlatform.Acceptance.IntegrationTests/DotnetTestTests.cs @@ -98,4 +98,21 @@ public void RunDotnetTestWithNativeDll(RunnerInfo runnerInfo) ValidateSummaryStatus(1, 1, 0); ExitCodeEquals(1); } + + [TestMethod] + // patched dotnet is not published on non-windows systems + [TestCategory("Windows-Review")] + [NetCoreTargetFrameworkDataSource(useDesktopRunner: false)] + public void RunDotnetTestAndSeeOutputFromConsoleWriteLine(RunnerInfo runnerInfo) + { + SetTestEnvironment(_testEnvironment, runnerInfo); + + var assemblyPath = GetAssetFullPath("OutputtingTestProject.dll"); + InvokeDotnetTest($@"{assemblyPath} --logger:""Console;Verbosity=normal"" "); + + StdOutputContains("MY OUTPUT FROM TEST"); + + ValidateSummaryStatus(1, 0, 0); + ExitCodeEquals(0); + } } diff --git a/test/Microsoft.TestPlatform.Client.UnitTests/DesignMode/DesignModeClientTests.cs b/test/Microsoft.TestPlatform.Client.UnitTests/DesignMode/DesignModeClientTests.cs index 59551d52b7..84acf7765a 100644 --- a/test/Microsoft.TestPlatform.Client.UnitTests/DesignMode/DesignModeClientTests.cs +++ b/test/Microsoft.TestPlatform.Client.UnitTests/DesignMode/DesignModeClientTests.cs @@ -22,7 +22,6 @@ using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces; using Moq; @@ -42,15 +41,13 @@ public class DesignModeClientTests private readonly int _protocolVersion = 7; private readonly AutoResetEvent _completeEvent; private readonly Mock _mockPlatformEnvironment; - private readonly Mock _mockEnvironmentVariableHelper; public DesignModeClientTests() { _mockTestRequestManager = new Mock(); _mockCommunicationManager = new Mock(); _mockPlatformEnvironment = new Mock(); - _mockEnvironmentVariableHelper = new Mock(); - _designModeClient = new DesignModeClient(_mockCommunicationManager.Object, JsonDataSerializer.Instance, _mockPlatformEnvironment.Object, _mockEnvironmentVariableHelper.Object); + _designModeClient = new DesignModeClient(_mockCommunicationManager.Object, JsonDataSerializer.Instance, _mockPlatformEnvironment.Object); _completeEvent = new AutoResetEvent(false); } @@ -295,7 +292,7 @@ public void DesignModeClientShouldStopCommunicationOnParentProcessExit() [TestMethod] public void DesignModeClientLaunchCustomHostMustReturnIfAckComes() { - var testableDesignModeClient = new TestableDesignModeClient(_mockCommunicationManager.Object, JsonDataSerializer.Instance, _mockPlatformEnvironment.Object, _mockEnvironmentVariableHelper.Object); + var testableDesignModeClient = new TestableDesignModeClient(_mockCommunicationManager.Object, JsonDataSerializer.Instance, _mockPlatformEnvironment.Object); _mockCommunicationManager.Setup(cm => cm.WaitForServerConnection(It.IsAny())).Returns(true); @@ -315,7 +312,7 @@ public void DesignModeClientLaunchCustomHostMustReturnIfAckComes() [ExpectedException(typeof(TestPlatformException))] public void DesignModeClientLaunchCustomHostMustThrowIfInvalidAckComes() { - var testableDesignModeClient = new TestableDesignModeClient(_mockCommunicationManager.Object, JsonDataSerializer.Instance, _mockPlatformEnvironment.Object, _mockEnvironmentVariableHelper.Object); + var testableDesignModeClient = new TestableDesignModeClient(_mockCommunicationManager.Object, JsonDataSerializer.Instance, _mockPlatformEnvironment.Object); _mockCommunicationManager.Setup(cm => cm.WaitForServerConnection(It.IsAny())).Returns(true); @@ -334,7 +331,7 @@ public void DesignModeClientLaunchCustomHostMustThrowIfInvalidAckComes() [ExpectedException(typeof(TestPlatformException))] public void DesignModeClientLaunchCustomHostMustThrowIfCancellationOccursBeforeHostLaunch() { - var testableDesignModeClient = new TestableDesignModeClient(_mockCommunicationManager.Object, JsonDataSerializer.Instance, _mockPlatformEnvironment.Object, _mockEnvironmentVariableHelper.Object); + var testableDesignModeClient = new TestableDesignModeClient(_mockCommunicationManager.Object, JsonDataSerializer.Instance, _mockPlatformEnvironment.Object); var info = new TestProcessStartInfo(); var cancellationTokenSource = new CancellationTokenSource(); @@ -635,9 +632,8 @@ private class TestableDesignModeClient : DesignModeClient internal TestableDesignModeClient( ICommunicationManager communicationManager, IDataSerializer dataSerializer, - IEnvironment platformEnvironment, - IEnvironmentVariableHelper environmentVariableHelper) - : base(communicationManager, dataSerializer, platformEnvironment, environmentVariableHelper) + IEnvironment platformEnvironment) + : base(communicationManager, dataSerializer, platformEnvironment) { } diff --git a/test/Microsoft.TestPlatform.Common.UnitTests/ExtensionFramework/ExtensionDecoratorTests.cs b/test/Microsoft.TestPlatform.Common.UnitTests/ExtensionFramework/ExtensionDecoratorTests.cs index ac1e85826a..2bb19a3ca6 100644 --- a/test/Microsoft.TestPlatform.Common.UnitTests/ExtensionFramework/ExtensionDecoratorTests.cs +++ b/test/Microsoft.TestPlatform.Common.UnitTests/ExtensionFramework/ExtensionDecoratorTests.cs @@ -38,7 +38,7 @@ public class ExtensionDecoratorTests public void ExtensionDecoratorFactory_DisabledByFlag() { // Arrange - _featureFlagMock.Setup(x => x.IsSet(FeatureFlag.DISABLE_SERIALTESTRUN_DECORATOR)).Returns(true); + _featureFlagMock.Setup(x => x.IsSet(FeatureFlag.VSTEST_DISABLE_SERIALTESTRUN_DECORATOR)).Returns(true); // Run test and assert ExtensionDecoratorFactory extensionDecoratorFactory = new(_featureFlagMock.Object); diff --git a/test/TestAssets/OutputtingTestProject/OutputtingTestProject.csproj b/test/TestAssets/OutputtingTestProject/OutputtingTestProject.csproj new file mode 100644 index 0000000000..9d5a70e0fa --- /dev/null +++ b/test/TestAssets/OutputtingTestProject/OutputtingTestProject.csproj @@ -0,0 +1,25 @@ + + + + + $(NetFrameworkMinimum);$(NetCoreAppMinimum) + Exe + x64 + + + + + + + + + + + + + + + + + + diff --git a/test/TestAssets/OutputtingTestProject/UnitTest1.cs b/test/TestAssets/OutputtingTestProject/UnitTest1.cs new file mode 100644 index 0000000000..d71da20d2a --- /dev/null +++ b/test/TestAssets/OutputtingTestProject/UnitTest1.cs @@ -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. + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using System; +using System.IO; + +namespace OutputtingTestProject; + +[TestClass] +public class UnitTest1 +{ + [TestMethod] + public void TestThatWritesOutput() + { + // This is a trick to bypass the console output capturing that MSTest does, and write directly + // to console output. The same bypass can be configured by false + // in MSTest 3.3.2 and newer. + using StreamWriter writer = new StreamWriter(Console.OpenStandardOutput()); + writer.WriteLine("MY OUTPUT FROM TEST"); + } +} diff --git a/test/TestAssets/TestAssets.sln b/test/TestAssets/TestAssets.sln index ac0dfefb2a..7556f991e1 100644 --- a/test/TestAssets/TestAssets.sln +++ b/test/TestAssets/TestAssets.sln @@ -134,6 +134,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetStandard2Library", "NetS EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TerminalLoggerTestProject", "TerminalLoggerTestProject\TerminalLoggerTestProject.csproj", "{C69BEEA4-BE37-4155-8DD0-130C62D8BF6D}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OutputtingTestProject", "OutputtingTestProject\OutputtingTestProject.csproj", "{A83CE873-2536-4795-B601-5816294ED0B8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -384,6 +386,10 @@ Global {C69BEEA4-BE37-4155-8DD0-130C62D8BF6D}.Debug|Any CPU.Build.0 = Debug|Any CPU {C69BEEA4-BE37-4155-8DD0-130C62D8BF6D}.Release|Any CPU.ActiveCfg = Release|Any CPU {C69BEEA4-BE37-4155-8DD0-130C62D8BF6D}.Release|Any CPU.Build.0 = Release|Any CPU + {A83CE873-2536-4795-B601-5816294ED0B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A83CE873-2536-4795-B601-5816294ED0B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A83CE873-2536-4795-B601-5816294ED0B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A83CE873-2536-4795-B601-5816294ED0B8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/test/vstest.ProgrammerTests/MultiTFMRunAndDiscovery.cs b/test/vstest.ProgrammerTests/MultiTFMRunAndDiscovery.cs index 1badc169dd..9ff6c075f7 100644 --- a/test/vstest.ProgrammerTests/MultiTFMRunAndDiscovery.cs +++ b/test/vstest.ProgrammerTests/MultiTFMRunAndDiscovery.cs @@ -561,7 +561,7 @@ public async Task E() { FeatureFlags = new Dictionary { - [FeatureFlag.DISABLE_MULTI_TFM_RUN] = true + [FeatureFlag.VSTEST_DISABLE_MULTI_TFM_RUN] = true } } );