From c2bc47143c1d7fa6b740b7e1ccfbcba12a024b01 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 01:58:40 +0000 Subject: [PATCH 01/10] Initial plan From 9f502f9c7ba1cc71ba2559516e02d575d89d098f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 02:29:30 +0000 Subject: [PATCH 02/10] Add FORCE_COLOR environment variable support to ConsoleUtils and ConsoleLoggerProvider Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../Common/src/System/Console/ConsoleUtils.cs | 27 +++++++------- .../src/ConsoleLoggerProvider.cs | 8 ++-- .../src/System/ConsolePal.Unix.cs | 4 +- src/libraries/System.Console/tests/Color.cs | 37 +++++++++++++------ 4 files changed, 46 insertions(+), 30 deletions(-) diff --git a/src/libraries/Common/src/System/Console/ConsoleUtils.cs b/src/libraries/Common/src/System/Console/ConsoleUtils.cs index f8472390d1f858..7da5e30a088205 100644 --- a/src/libraries/Common/src/System/Console/ConsoleUtils.cs +++ b/src/libraries/Common/src/System/Console/ConsoleUtils.cs @@ -5,7 +5,7 @@ namespace System { internal static partial class ConsoleUtils { - /// Whether to output ansi color strings. + /// Whether to output ANSI color strings. private static volatile int s_emitAnsiColorCodes = -1; /// Get whether to emit ANSI color codes. @@ -13,34 +13,35 @@ public static bool EmitAnsiColorCodes { get { - // The flag starts at -1. If it's no longer -1, it's 0 or 1 to represent false or true. + // The flag starts at -1. If it's no longer -1, it's 0 or 1 to represent false or true. int emitAnsiColorCodes = s_emitAnsiColorCodes; if (emitAnsiColorCodes != -1) { return Convert.ToBoolean(emitAnsiColorCodes); } - // We've not yet computed whether to emit codes or not. Do so now. We may race with + // We've not yet computed whether to emit codes or not. We may race with // other threads, and that's ok; this is idempotent unless someone is currently changing // the value of the relevant environment variables, in which case behavior here is undefined. + // Per https://force-color.org/, FORCE_COLOR forces ANSI color output when set to a non-empty value. + // DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION is treated as a legacy alias for the same behavior. + // These take highest priority and override all other checks. + if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("FORCE_COLOR")) || + !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION"))) + { + s_emitAnsiColorCodes = 1; + return true; + } + // By default, we emit ANSI color codes if output isn't redirected, and suppress them if output is redirected. bool enabled = !Console.IsOutputRedirected; if (enabled) { - // We subscribe to the informal standard from https://no-color.org/. If we'd otherwise emit - // ANSI color codes but the NO_COLOR environment variable is set, disable emitting them. + // Per https://no-color.org/, NO_COLOR disables ANSI color output when set. enabled = Environment.GetEnvironmentVariable("NO_COLOR") is null; } - else - { - // We also support overriding in the other direction. If we'd otherwise avoid emitting color - // codes but the DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION environment variable is - // set to 1 or true, enable color. - string? envVar = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION"); - enabled = envVar is not null && (envVar == "1" || envVar.Equals("true", StringComparison.OrdinalIgnoreCase)); - } // Store and return the computed answer. s_emitAnsiColorCodes = Convert.ToInt32(enabled); diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLoggerProvider.cs b/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLoggerProvider.cs index 68a356de9ba748..ff800908a7f438 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLoggerProvider.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLoggerProvider.cs @@ -68,11 +68,11 @@ public ConsoleLoggerProvider(IOptionsMonitor options, IEnu [UnsupportedOSPlatformGuard("windows")] private static bool DoesConsoleSupportAnsi() { - string? envVar = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION"); - if (envVar is not null && (envVar == "1" || envVar.Equals("true", StringComparison.OrdinalIgnoreCase))) + // Per https://force-color.org/, FORCE_COLOR forces ANSI color output when set to a non-empty value. + // DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION is treated as a legacy alias for the same behavior. + if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("FORCE_COLOR")) || + !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION"))) { - // ANSI color support forcibly enabled via environment variable. This logic matches the behaviour - // found in System.ConsoleUtils.EmitAnsiColorCodes. return true; } if ( diff --git a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs index 57d4ef11fd62e3..7440caa7885322 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs @@ -1112,8 +1112,8 @@ private static void InvalidateTerminalSettings() Volatile.Write(ref s_invalidateCachedSettings, 1); } - // Ansi colors are enabled when stdout is a terminal or when - // DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION is set. + // Ansi colors are enabled when stdout is a terminal, when + // FORCE_COLOR is set, or when DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION is set. // In both cases, they are written to stdout. internal static void WriteTerminalAnsiColorString(string? value) => WriteTerminalAnsiString(value, OpenStandardOutputHandle(), mayChangeCursorPosition: false); diff --git a/src/libraries/System.Console/tests/Color.cs b/src/libraries/System.Console/tests/Color.cs index 0c43c50c03b424..b8fbaa7ace6ad8 100644 --- a/src/libraries/System.Console/tests/Color.cs +++ b/src/libraries/System.Console/tests/Color.cs @@ -78,16 +78,33 @@ public static bool TermIsSetAndRemoteExecutorIsSupported [ConditionalTheory(nameof(TermIsSetAndRemoteExecutorIsSupported))] [PlatformSpecific(TestPlatforms.AnyUnix)] [SkipOnPlatform(TestPlatforms.Browser | TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "Not supported on Browser, iOS, MacCatalyst, or tvOS.")] - [InlineData(null)] - [InlineData("1")] - [InlineData("true")] - [InlineData("tRuE")] - [InlineData("0")] - [InlineData("false")] - public static void RedirectedOutput_EnvVarSet_EmitsAnsiCodes(string? envVar) + [InlineData("DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION", "1", null, null, true)] + [InlineData("DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION", "true", null, null, true)] + [InlineData("DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION", "tRuE", null, null, true)] + [InlineData("DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION", "0", null, null, true)] + [InlineData("DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION", "any-value", null, null, true)] + [InlineData(null, null, "FORCE_COLOR", "1", true)] + [InlineData(null, null, "FORCE_COLOR", "true", true)] + [InlineData(null, null, "FORCE_COLOR", "any-value", true)] + [InlineData(null, null, "NO_COLOR", "1", false)] + [InlineData(null, null, "NO_COLOR", "true", false)] + [InlineData(null, null, "NO_COLOR", "any-value", false)] + [InlineData("FORCE_COLOR", "1", "NO_COLOR", "1", true)] + [InlineData("DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION", "1", "NO_COLOR", "1", true)] + public static void RedirectedOutput_ColorEnvVars_RespectColorPreference( + string? envVarName1, string? envVarValue1, + string? envVarName2, string? envVarValue2, + bool shouldEmitEscapes) { var psi = new ProcessStartInfo { RedirectStandardOutput = true }; - psi.Environment["DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION"] = envVar; + if (envVarName1 is not null) + { + psi.Environment[envVarName1] = envVarValue1; + } + if (envVarName2 is not null) + { + psi.Environment[envVarName2] = envVarValue2; + } for (int i = 0; i < 3; i++) { @@ -113,13 +130,11 @@ public static void RedirectedOutput_EnvVarSet_EmitsAnsiCodes(string? envVar) using RemoteInvokeHandle remote = RemoteExecutor.Invoke(main, i.ToString(CultureInfo.InvariantCulture), new RemoteInvokeOptions() { StartInfo = psi }); - bool expectedEscapes = envVar is not null && (envVar == "1" || envVar.Equals("true", StringComparison.OrdinalIgnoreCase)); - string stdout = remote.Process.StandardOutput.ReadToEnd(); string[] parts = stdout.Split("SEPARATOR"); Assert.Equal(3, parts.Length); - Assert.Equal(expectedEscapes, parts[1].Contains(Esc)); + Assert.Equal(shouldEmitEscapes, parts[1].Contains(Esc)); } } } From 961225e002fb8bf1f82469ded8bc3b993df39458 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 02:32:23 +0000 Subject: [PATCH 03/10] Fix comment: 'In all cases' instead of 'In both cases' since there are now three trigger conditions Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- src/libraries/System.Console/src/System/ConsolePal.Unix.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs index 7440caa7885322..0aef24b28da7c7 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs @@ -1114,7 +1114,7 @@ private static void InvalidateTerminalSettings() // Ansi colors are enabled when stdout is a terminal, when // FORCE_COLOR is set, or when DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION is set. - // In both cases, they are written to stdout. + // In all cases, they are written to stdout. internal static void WriteTerminalAnsiColorString(string? value) => WriteTerminalAnsiString(value, OpenStandardOutputHandle(), mayChangeCursorPosition: false); From 9659c7e1a5b78a78058d400badb73f6a5bf954f7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 03:20:17 +0000 Subject: [PATCH 04/10] Add NO_COLOR support to ConsoleLoggerProvider.DoesConsoleSupportAnsi Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../src/ConsoleLoggerProvider.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLoggerProvider.cs b/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLoggerProvider.cs index ff800908a7f438..f05da9785bd457 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLoggerProvider.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLoggerProvider.cs @@ -75,6 +75,13 @@ private static bool DoesConsoleSupportAnsi() { return true; } + + // Per https://no-color.org/, NO_COLOR disables ANSI color output when set. + if (Environment.GetEnvironmentVariable("NO_COLOR") is not null) + { + return false; + } + if ( #if NETFRAMEWORK Environment.OSVersion.Platform != PlatformID.Win32NT From 78ee4df365de026a06a774db9f86f928121970dc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 08:09:32 +0000 Subject: [PATCH 05/10] Add empty-string test cases and M.E.Logging.Console tests for color env vars Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../ConsoleLoggerTest.cs | 37 +++++++++++++++++++ src/libraries/System.Console/tests/Color.cs | 2 + 2 files changed, 39 insertions(+) diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/ConsoleLoggerTest.cs b/src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/ConsoleLoggerTest.cs index f48a4a0e1176ab..792ace00e97a47 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/ConsoleLoggerTest.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/ConsoleLoggerTest.cs @@ -445,6 +445,43 @@ public void AddConsole_IsOutputRedirected_ColorDisabled() }, new RemoteInvokeOptions { StartInfo = new ProcessStartInfo() { RedirectStandardOutput = true } }).Dispose(); } + [ConditionalTheory(nameof(IsThreadingAndRemoteExecutorSupported))] + [InlineData("FORCE_COLOR", "1", null, null, "AnsiLogConsole")] + [InlineData("DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION", "1", null, null, "AnsiLogConsole")] + [InlineData(null, null, "NO_COLOR", "1", "AnsiParsingLogConsole")] + [InlineData("FORCE_COLOR", "1", "NO_COLOR", "1", "AnsiLogConsole")] + public void DoesConsoleSupportAnsi_RespectsColorEnvVars( + string? envVarName1, string? envVarValue1, + string? envVarName2, string? envVarValue2, + string expectedConsoleTypeName) + { + var psi = new ProcessStartInfo { RedirectStandardOutput = true }; + if (envVarName1 is not null) + { + psi.Environment[envVarName1] = envVarValue1; + } + if (envVarName2 is not null) + { + psi.Environment[envVarName2] = envVarValue2; + } + + RemoteExecutor.Invoke(static (expectedTypeName) => + { + var loggerProvider = new ServiceCollection() + .AddLogging(builder => builder.AddConsole()) + .BuildServiceProvider() + .GetRequiredService(); + + var consoleLoggerProvider = Assert.IsType(loggerProvider); + + var messageQueueField = typeof(ConsoleLoggerProvider).GetField("_messageQueue", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + var processor = (ConsoleLoggerProcessor)messageQueueField!.GetValue(consoleLoggerProvider)!; + Assert.Equal(expectedTypeName, processor.Console.GetType().Name); + + loggerProvider.Dispose(); + }, expectedConsoleTypeName, new RemoteInvokeOptions { StartInfo = psi }).Dispose(); + } + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void WriteDebug_LogsCorrectColors() { diff --git a/src/libraries/System.Console/tests/Color.cs b/src/libraries/System.Console/tests/Color.cs index b8fbaa7ace6ad8..8b0f50feaff161 100644 --- a/src/libraries/System.Console/tests/Color.cs +++ b/src/libraries/System.Console/tests/Color.cs @@ -83,9 +83,11 @@ public static bool TermIsSetAndRemoteExecutorIsSupported [InlineData("DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION", "tRuE", null, null, true)] [InlineData("DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION", "0", null, null, true)] [InlineData("DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION", "any-value", null, null, true)] + [InlineData("DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION", "", null, null, false)] [InlineData(null, null, "FORCE_COLOR", "1", true)] [InlineData(null, null, "FORCE_COLOR", "true", true)] [InlineData(null, null, "FORCE_COLOR", "any-value", true)] + [InlineData(null, null, "FORCE_COLOR", "", false)] [InlineData(null, null, "NO_COLOR", "1", false)] [InlineData(null, null, "NO_COLOR", "true", false)] [InlineData(null, null, "NO_COLOR", "any-value", false)] From b6179b3228d71f9db5dad7a246eff35d9721d53f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 11:50:14 +0000 Subject: [PATCH 06/10] Fix RedirectedOutputDoesNotUseAnsiSequences test to tolerate extraneous console output from test framework Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- src/libraries/System.Console/tests/Color.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Console/tests/Color.cs b/src/libraries/System.Console/tests/Color.cs index 8b0f50feaff161..e087ddf8a60fc1 100644 --- a/src/libraries/System.Console/tests/Color.cs +++ b/src/libraries/System.Console/tests/Color.cs @@ -67,8 +67,9 @@ public static void RedirectedOutputDoesNotUseAnsiSequences() Console.ResetColor(); Console.Write('4'); - Assert.Equal(0, Encoding.UTF8.GetString(data.ToArray()).ToCharArray().Count(c => c == Esc)); - Assert.Equal("1234", Encoding.UTF8.GetString(data.ToArray())); + string outputText = Encoding.UTF8.GetString(data.ToArray()); + Assert.Equal(0, outputText.Count(c => c == Esc)); + Assert.StartsWith("1234", outputText); }); } From 34d0d304345b2fd2ace1de0f47f47664dbc9dc1c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 13:05:11 +0000 Subject: [PATCH 07/10] Fix RedirectedOutputDoesNotUseAnsiSequences: flush Console.Out before reading and restore exact Assert.Equal Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- src/libraries/System.Console/tests/Color.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Console/tests/Color.cs b/src/libraries/System.Console/tests/Color.cs index e087ddf8a60fc1..d537e9494f5aa6 100644 --- a/src/libraries/System.Console/tests/Color.cs +++ b/src/libraries/System.Console/tests/Color.cs @@ -67,9 +67,10 @@ public static void RedirectedOutputDoesNotUseAnsiSequences() Console.ResetColor(); Console.Write('4'); + Console.Out.Flush(); string outputText = Encoding.UTF8.GetString(data.ToArray()); Assert.Equal(0, outputText.Count(c => c == Esc)); - Assert.StartsWith("1234", outputText); + Assert.Equal("1234", outputText); }); } From 0ae627e22b122d972f4c38f75b0d6d9b67b05b20 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Feb 2026 10:46:55 +0000 Subject: [PATCH 08/10] Revert RedirectedOutputDoesNotUseAnsiSequences to original form and fix ConditionalTheory attribute style Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- src/libraries/System.Console/tests/Color.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Console/tests/Color.cs b/src/libraries/System.Console/tests/Color.cs index 9d56eb61fbabe3..8b0f50feaff161 100644 --- a/src/libraries/System.Console/tests/Color.cs +++ b/src/libraries/System.Console/tests/Color.cs @@ -67,17 +67,15 @@ public static void RedirectedOutputDoesNotUseAnsiSequences() Console.ResetColor(); Console.Write('4'); - Console.Out.Flush(); - string outputText = Encoding.UTF8.GetString(data.ToArray()); - Assert.Equal(0, outputText.Count(c => c == Esc)); - Assert.Equal("1234", outputText); + Assert.Equal(0, Encoding.UTF8.GetString(data.ToArray()).ToCharArray().Count(c => c == Esc)); + Assert.Equal("1234", Encoding.UTF8.GetString(data.ToArray())); }); } public static bool TermIsSetAndRemoteExecutorIsSupported => !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TERM")) && RemoteExecutor.IsSupported; - [ConditionalTheory(typeof(Color), nameof(TermIsSetAndRemoteExecutorIsSupported))] + [ConditionalTheory(nameof(TermIsSetAndRemoteExecutorIsSupported))] [PlatformSpecific(TestPlatforms.AnyUnix)] [SkipOnPlatform(TestPlatforms.Browser | TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "Not supported on Browser, iOS, MacCatalyst, or tvOS.")] [InlineData("DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION", "1", null, null, true)] From 5f6818671564ff699a6beefe53f69fcc37c64297 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Feb 2026 14:38:13 +0000 Subject: [PATCH 09/10] Changes before error encountered Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- src/libraries/System.Console/tests/Color.cs | 26 +++++++++++---------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.Console/tests/Color.cs b/src/libraries/System.Console/tests/Color.cs index 8b0f50feaff161..6b57d5b7ae8b40 100644 --- a/src/libraries/System.Console/tests/Color.cs +++ b/src/libraries/System.Console/tests/Color.cs @@ -51,31 +51,33 @@ public static void BackgroundColor_Throws_PlatformNotSupportedException() Assert.Throws(() => Console.BackgroundColor = ConsoleColor.Red); } - [Fact] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [SkipOnPlatform(TestPlatforms.Browser | TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "Not supported on Browser, iOS, MacCatalyst, or tvOS.")] public static void RedirectedOutputDoesNotUseAnsiSequences() { - // Make sure that redirecting to a memory stream causes Console not to write out the ANSI sequences - - Helpers.RunInRedirectedOutput((data) => + // Run in a child process with redirected stdout so that no in-process + // test framework output (e.g. xunit skip messages) can pollute the stream. + var startInfo = new ProcessStartInfo { RedirectStandardOutput = true }; + using RemoteInvokeHandle handle = RemoteExecutor.Invoke(static () => { - Console.Write('1'); + Console.Write("1"); Console.ForegroundColor = ConsoleColor.Blue; - Console.Write('2'); + Console.Write("2"); Console.BackgroundColor = ConsoleColor.Red; - Console.Write('3'); + Console.Write("3"); Console.ResetColor(); - Console.Write('4'); + Console.Write("4"); + }, new RemoteInvokeOptions { StartInfo = startInfo }); - Assert.Equal(0, Encoding.UTF8.GetString(data.ToArray()).ToCharArray().Count(c => c == Esc)); - Assert.Equal("1234", Encoding.UTF8.GetString(data.ToArray())); - }); + string capturedOutput = handle.Process.StandardOutput.ReadToEnd(); + Assert.DoesNotContain(Esc.ToString(), capturedOutput); + Assert.Equal("1234", capturedOutput); } public static bool TermIsSetAndRemoteExecutorIsSupported => !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TERM")) && RemoteExecutor.IsSupported; - [ConditionalTheory(nameof(TermIsSetAndRemoteExecutorIsSupported))] + [ConditionalTheory(typeof(Color), nameof(TermIsSetAndRemoteExecutorIsSupported))] [PlatformSpecific(TestPlatforms.AnyUnix)] [SkipOnPlatform(TestPlatforms.Browser | TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "Not supported on Browser, iOS, MacCatalyst, or tvOS.")] [InlineData("DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION", "1", null, null, true)] From 4161212488e357310a160d3fe9ac2ca06b286a2c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 17:43:20 +0000 Subject: [PATCH 10/10] Changes before error encountered Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- src/libraries/System.Console/tests/Color.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/libraries/System.Console/tests/Color.cs b/src/libraries/System.Console/tests/Color.cs index 6b57d5b7ae8b40..520ebadde8366c 100644 --- a/src/libraries/System.Console/tests/Color.cs +++ b/src/libraries/System.Console/tests/Color.cs @@ -60,6 +60,8 @@ public static void RedirectedOutputDoesNotUseAnsiSequences() var startInfo = new ProcessStartInfo { RedirectStandardOutput = true }; using RemoteInvokeHandle handle = RemoteExecutor.Invoke(static () => { + Console.Error.WriteLine($"IsOutputRedirected: {Console.IsOutputRedirected}"); + Console.Error.WriteLine($"TERM: {Environment.GetEnvironmentVariable("TERM")}"); Console.Write("1"); Console.ForegroundColor = ConsoleColor.Blue; Console.Write("2"); @@ -67,9 +69,26 @@ public static void RedirectedOutputDoesNotUseAnsiSequences() Console.Write("3"); Console.ResetColor(); Console.Write("4"); + Console.Error.WriteLine($"Done writing"); }, new RemoteInvokeOptions { StartInfo = startInfo }); string capturedOutput = handle.Process.StandardOutput.ReadToEnd(); + byte[] rawBytes = System.Text.Encoding.UTF8.GetBytes(capturedOutput); + Console.Error.WriteLine($"Output length: {rawBytes.Length}"); + Console.Error.WriteLine($"Output hex: {BitConverter.ToString(rawBytes)}"); + Console.Error.Write("Output chars: "); + foreach (char c in capturedOutput) + { + if (c >= 32 && c < 127) + Console.Error.Write(c); + else + Console.Error.Write($"[0x{(int)c:X2}]"); + } + Console.Error.WriteLine(); + Console.Error.WriteLine($"capturedOutput[0] = 0x{(int)capturedOutput[0]:X2}"); + Console.Error.WriteLine($"capturedOutput == \"1234\": {capturedOutput == "1234"}"); + Console.Error.WriteLine($"Contains ESC: {capturedOutput.Contains(Esc)}"); + Console.Error.WriteLine($"Esc char value: 0x{(int)Esc:X2}"); Assert.DoesNotContain(Esc.ToString(), capturedOutput); Assert.Equal("1234", capturedOutput); }