Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// 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.CommandLine;

/// <summary>
/// Provides validation helpers for command line option arguments that accept boolean-like values.
/// </summary>
internal static class CommandLineOptionArgumentValidator
{
private const string AutoValue = "auto";
private static readonly string[] OnValues = ["on", "true", "enable", "1"];
private static readonly string[] OffValues = ["off", "false", "disable", "0"];

/// <summary>
/// Determines whether the argument is one of the accepted on/off boolean values.
/// </summary>
public static bool IsValidBooleanArgument(string argument)
=> IsOnValue(argument) || IsOffValue(argument);

/// <summary>
/// Determines whether the argument is one of the accepted on/off/auto values.
/// </summary>
public static bool IsValidBooleanAutoArgument(string argument)
=> AutoValue.Equals(argument, StringComparison.OrdinalIgnoreCase)
|| IsValidBooleanArgument(argument);

/// <summary>
/// Determines whether the argument represents an enabled state (<c>on</c>, <c>true</c>, <c>enable</c>, <c>1</c>).
/// </summary>
public static bool IsOnValue(string argument)
=> OnValues.Any(onValue => onValue.Equals(argument, StringComparison.OrdinalIgnoreCase));

/// <summary>
/// Determines whether the argument represents a disabled state (<c>off</c>, <c>false</c>, <c>disable</c>, <c>0</c>).
/// </summary>
public static bool IsOffValue(string argument)
=> OffValues.Any(offValue => offValue.Equals(argument, StringComparison.OrdinalIgnoreCase));

/// <summary>
/// Determines whether the argument represents the <c>auto</c> value.
/// </summary>
public static bool IsAutoValue(string argument)
=> AutoValue.Equals(argument, StringComparison.OrdinalIgnoreCase);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ internal sealed class TerminalTestReporterCommandLineOptionsProvider : ICommandL
{
public const string NoProgressOption = "no-progress";
public const string NoAnsiOption = "no-ansi";
public const string AnsiOption = "ansi";
public const string OutputOption = "output";
public const string OutputOptionNormalArgument = "normal";
public const string OutputOptionDetailedArgument = "detailed";
Expand Down Expand Up @@ -42,6 +43,7 @@ public IReadOnlyCollection<CommandLineOption> GetCommandLineOptions()
[
new(NoProgressOption, PlatformResources.TerminalNoProgressOptionDescription, ArgumentArity.Zero, isHidden: false),
new(NoAnsiOption, PlatformResources.TerminalNoAnsiOptionDescription, ArgumentArity.Zero, isHidden: false),
new(AnsiOption, PlatformResources.TerminalAnsiOptionDescription, ArgumentArity.ExactlyOne, isHidden: false),
new(OutputOption, PlatformResources.TerminalOutputOptionDescription, ArgumentArity.ExactlyOne, isHidden: false),
new(ShowStdoutOption, PlatformResources.TerminalShowStdoutOptionDescription, ArgumentArity.ExactlyOne, isHidden: false),
new(ShowStderrOption, PlatformResources.TerminalShowStderrOptionDescription, ArgumentArity.ExactlyOne, isHidden: false),
Expand All @@ -52,6 +54,9 @@ public Task<ValidationResult> ValidateOptionArgumentsAsync(CommandLineOption com
{
NoProgressOption => ValidationResult.ValidTask,
NoAnsiOption => ValidationResult.ValidTask,
AnsiOption => arguments.Length == 1 && CommandLineOptionArgumentValidator.IsValidBooleanAutoArgument(arguments[0])
? ValidationResult.ValidTask
: ValidationResult.InvalidTask(PlatformResources.TerminalAnsiOptionInvalidArgument),
OutputOption => OutputOptionNormalArgument.Equals(arguments[0], StringComparison.OrdinalIgnoreCase) || OutputOptionDetailedArgument.Equals(arguments[0], StringComparison.OrdinalIgnoreCase)
? ValidationResult.ValidTask
: ValidationResult.InvalidTask(PlatformResources.TerminalOutputOptionInvalidArgument),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,21 +142,42 @@ await _policiesService.RegisterOnAbortCallbackAsync(
_isServerMode = _commandLineOptions.IsOptionSet(PlatformCommandLineProvider.ServerOptionKey);
bool noAnsi = _commandLineOptions.IsOptionSet(TerminalTestReporterCommandLineOptionsProvider.NoAnsiOption);

bool inCI = new CIEnvironmentDetector(_environment).IsCIEnvironment();

AnsiMode ansiMode = AnsiMode.AnsiIfPossible;
// In LLM environments, prefer simple text output so that LLM can parse it easily.
// Note that NoAnsi also implies no progress.
if (noAnsi || LLMEnvironmentDetector.IsLLMEnvironment())
// --ansi <auto|on|off>, when present (with any value), takes precedence over the legacy --no-ansi flag.
// This keeps the help text honest: passing --ansi at all overrides --no-ansi.
AnsiOverride ansiOverride = AnsiOverride.None;
if (_commandLineOptions.TryGetOptionArgumentList(TerminalTestReporterCommandLineOptionsProvider.AnsiOption, out string[]? ansiArguments)
&& ansiArguments is { Length: > 0 })
{
// User explicitly specified --no-ansi.
// We should respect that.
ansiMode = AnsiMode.NoAnsi;
string ansiValue = ansiArguments[0];
ansiOverride = CommandLineOptionArgumentValidator.IsOnValue(ansiValue)
? AnsiOverride.ForceOn
: CommandLineOptionArgumentValidator.IsOffValue(ansiValue)
? AnsiOverride.ForceOff
: AnsiOverride.Auto;
}
else if (inCI)

// When --ansi auto is explicitly specified, it overrides --no-ansi too.
bool effectiveNoAnsi = noAnsi && ansiOverride == AnsiOverride.None;

bool inCI = new CIEnvironmentDetector(_environment).IsCIEnvironment();

AnsiMode ansiMode = ansiOverride switch
{
ansiMode = AnsiMode.SimpleAnsi;
}
// User explicitly forced ANSI on (e.g. `--ansi on`). Bypass CI / LLM / redirection detection
// so colors and cursor movement are emitted even when stdout is redirected.
AnsiOverride.ForceOn => AnsiMode.ForceAnsi,

// User explicitly disabled ANSI (`--ansi off`).
// Note that NoAnsi also implies no progress.
AnsiOverride.ForceOff => AnsiMode.NoAnsi,

// No --ansi argument was provided, or `--ansi auto` was provided.
// Fall back to environment-based detection.
// In LLM environments, prefer simple text output so that the LLM can parse it easily.
_ when effectiveNoAnsi || LLMEnvironmentDetector.IsLLMEnvironment() => AnsiMode.NoAnsi,
_ when inCI => AnsiMode.SimpleAnsi,
_ => AnsiMode.AnsiIfPossible,
};

bool noProgress = _commandLineOptions.IsOptionSet(TerminalTestReporterCommandLineOptionsProvider.NoProgressOption);

Expand Down Expand Up @@ -214,6 +235,30 @@ string s when TerminalTestReporterCommandLineOptionsProvider.ShowOutputNoneArgum
}
: OutputShowMode.All;

private enum AnsiOverride
{
/// <summary>
/// The <c>--ansi</c> option was not provided.
/// </summary>
None,

/// <summary>
/// The <c>--ansi auto</c> option was provided. The user explicitly opts in to auto-detection
/// (and out of the legacy <c>--no-ansi</c> flag if it was also passed).
/// </summary>
Auto,

/// <summary>
/// The <c>--ansi on</c> option was provided, forcing ANSI output regardless of environment.
/// </summary>
ForceOn,

/// <summary>
/// The <c>--ansi off</c> option was provided, disabling ANSI output.
/// </summary>
ForceOff,
}

private static string GetShortArchitecture(string runtimeIdentifier)
{
int firstIndexOfDash = runtimeIdentifier.IndexOf(Dash);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,15 @@ Read more about Microsoft Testing Platform telemetry: https://aka.ms/testingplat
<data name="TerminalNoAnsiOptionDescription" xml:space="preserve">
<value>Disable outputting ANSI escape characters to screen.</value>
</data>
<data name="TerminalAnsiOptionDescription" xml:space="preserve">
<value>Control whether ANSI escape characters are emitted.
Valid values are 'auto' (default), 'on' (also accepts 'true', 'enable', '1') or 'off' (also accepts 'false', 'disable', '0').
'on' forces ANSI escape codes (including cursor movement) even when stdout is redirected; pair it with --no-progress if you only want colors.
When both --ansi and --no-ansi are provided, --ansi wins.</value>
</data>
<data name="TerminalAnsiOptionInvalidArgument" xml:space="preserve">
<value>--ansi expects a single parameter with value 'auto', 'on' (or 'true', 'enable', '1') or 'off' (or 'false', 'disable', '0').</value>
</data>
<data name="TerminalNoProgressOptionDescription" xml:space="preserve">
<value>Disable reporting progress to screen.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,22 @@ Přečtěte si další informace o telemetrii Microsoft Testing Platform: https:
<target state="translated">Zprostředkovatel telemetrie je už nastavený.</target>
<note />
</trans-unit>
<trans-unit id="TerminalAnsiOptionDescription">
<source>Control whether ANSI escape characters are emitted.
Valid values are 'auto' (default), 'on' (also accepts 'true', 'enable', '1') or 'off' (also accepts 'false', 'disable', '0').
'on' forces ANSI escape codes (including cursor movement) even when stdout is redirected; pair it with --no-progress if you only want colors.
When both --ansi and --no-ansi are provided, --ansi wins.</source>
<target state="new">Control whether ANSI escape characters are emitted.
Valid values are 'auto' (default), 'on' (also accepts 'true', 'enable', '1') or 'off' (also accepts 'false', 'disable', '0').
'on' forces ANSI escape codes (including cursor movement) even when stdout is redirected; pair it with --no-progress if you only want colors.
When both --ansi and --no-ansi are provided, --ansi wins.</target>
<note />
</trans-unit>
<trans-unit id="TerminalAnsiOptionInvalidArgument">
<source>--ansi expects a single parameter with value 'auto', 'on' (or 'true', 'enable', '1') or 'off' (or 'false', 'disable', '0').</source>
<target state="new">--ansi expects a single parameter with value 'auto', 'on' (or 'true', 'enable', '1') or 'off' (or 'false', 'disable', '0').</target>
<note />
</trans-unit>
<trans-unit id="TerminalNoAnsiOptionDescription">
<source>Disable outputting ANSI escape characters to screen.</source>
<target state="translated">Zakažte výstup řídicích znaků ANSI na obrazovku.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,22 @@ Weitere Informationen zu Microsoft Testing Platform-Telemetriedaten: https://aka
<target state="translated">Der Telemetrieanbieter ist bereits festgelegt</target>
<note />
</trans-unit>
<trans-unit id="TerminalAnsiOptionDescription">
<source>Control whether ANSI escape characters are emitted.
Valid values are 'auto' (default), 'on' (also accepts 'true', 'enable', '1') or 'off' (also accepts 'false', 'disable', '0').
'on' forces ANSI escape codes (including cursor movement) even when stdout is redirected; pair it with --no-progress if you only want colors.
When both --ansi and --no-ansi are provided, --ansi wins.</source>
<target state="new">Control whether ANSI escape characters are emitted.
Valid values are 'auto' (default), 'on' (also accepts 'true', 'enable', '1') or 'off' (also accepts 'false', 'disable', '0').
'on' forces ANSI escape codes (including cursor movement) even when stdout is redirected; pair it with --no-progress if you only want colors.
When both --ansi and --no-ansi are provided, --ansi wins.</target>
<note />
</trans-unit>
<trans-unit id="TerminalAnsiOptionInvalidArgument">
<source>--ansi expects a single parameter with value 'auto', 'on' (or 'true', 'enable', '1') or 'off' (or 'false', 'disable', '0').</source>
<target state="new">--ansi expects a single parameter with value 'auto', 'on' (or 'true', 'enable', '1') or 'off' (or 'false', 'disable', '0').</target>
<note />
</trans-unit>
<trans-unit id="TerminalNoAnsiOptionDescription">
<source>Disable outputting ANSI escape characters to screen.</source>
<target state="translated">Deaktivieren Sie die Ausgabe von ANSI-Escape-Zeichen auf dem Bildschirm.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,22 @@ Más información sobre la telemetría de la Plataforma de pruebas de Microsoft:
<target state="translated">El proveedor de telemetría ya está establecido</target>
<note />
</trans-unit>
<trans-unit id="TerminalAnsiOptionDescription">
<source>Control whether ANSI escape characters are emitted.
Valid values are 'auto' (default), 'on' (also accepts 'true', 'enable', '1') or 'off' (also accepts 'false', 'disable', '0').
'on' forces ANSI escape codes (including cursor movement) even when stdout is redirected; pair it with --no-progress if you only want colors.
When both --ansi and --no-ansi are provided, --ansi wins.</source>
<target state="new">Control whether ANSI escape characters are emitted.
Valid values are 'auto' (default), 'on' (also accepts 'true', 'enable', '1') or 'off' (also accepts 'false', 'disable', '0').
'on' forces ANSI escape codes (including cursor movement) even when stdout is redirected; pair it with --no-progress if you only want colors.
When both --ansi and --no-ansi are provided, --ansi wins.</target>
<note />
</trans-unit>
<trans-unit id="TerminalAnsiOptionInvalidArgument">
<source>--ansi expects a single parameter with value 'auto', 'on' (or 'true', 'enable', '1') or 'off' (or 'false', 'disable', '0').</source>
<target state="new">--ansi expects a single parameter with value 'auto', 'on' (or 'true', 'enable', '1') or 'off' (or 'false', 'disable', '0').</target>
<note />
</trans-unit>
<trans-unit id="TerminalNoAnsiOptionDescription">
<source>Disable outputting ANSI escape characters to screen.</source>
<target state="translated">Deshabilite la salida de caracteres de escape ANSI en la pantalla.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,22 @@ En savoir plus sur la télémétrie de la plateforme de tests Microsoft : https:
<target state="translated">Le fournisseur de télémétrie est déjà défini</target>
<note />
</trans-unit>
<trans-unit id="TerminalAnsiOptionDescription">
<source>Control whether ANSI escape characters are emitted.
Valid values are 'auto' (default), 'on' (also accepts 'true', 'enable', '1') or 'off' (also accepts 'false', 'disable', '0').
'on' forces ANSI escape codes (including cursor movement) even when stdout is redirected; pair it with --no-progress if you only want colors.
When both --ansi and --no-ansi are provided, --ansi wins.</source>
<target state="new">Control whether ANSI escape characters are emitted.
Valid values are 'auto' (default), 'on' (also accepts 'true', 'enable', '1') or 'off' (also accepts 'false', 'disable', '0').
'on' forces ANSI escape codes (including cursor movement) even when stdout is redirected; pair it with --no-progress if you only want colors.
When both --ansi and --no-ansi are provided, --ansi wins.</target>
<note />
</trans-unit>
<trans-unit id="TerminalAnsiOptionInvalidArgument">
<source>--ansi expects a single parameter with value 'auto', 'on' (or 'true', 'enable', '1') or 'off' (or 'false', 'disable', '0').</source>
<target state="new">--ansi expects a single parameter with value 'auto', 'on' (or 'true', 'enable', '1') or 'off' (or 'false', 'disable', '0').</target>
<note />
</trans-unit>
<trans-unit id="TerminalNoAnsiOptionDescription">
<source>Disable outputting ANSI escape characters to screen.</source>
<target state="translated">Désactiver la sortie des caractères d’échappement ANSI à l’écran.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,22 @@ Altre informazioni sulla telemetria della piattaforma di test Microsoft: https:/
<target state="translated">Il provider di telemetria è già impostato</target>
<note />
</trans-unit>
<trans-unit id="TerminalAnsiOptionDescription">
<source>Control whether ANSI escape characters are emitted.
Valid values are 'auto' (default), 'on' (also accepts 'true', 'enable', '1') or 'off' (also accepts 'false', 'disable', '0').
'on' forces ANSI escape codes (including cursor movement) even when stdout is redirected; pair it with --no-progress if you only want colors.
When both --ansi and --no-ansi are provided, --ansi wins.</source>
<target state="new">Control whether ANSI escape characters are emitted.
Valid values are 'auto' (default), 'on' (also accepts 'true', 'enable', '1') or 'off' (also accepts 'false', 'disable', '0').
'on' forces ANSI escape codes (including cursor movement) even when stdout is redirected; pair it with --no-progress if you only want colors.
When both --ansi and --no-ansi are provided, --ansi wins.</target>
<note />
</trans-unit>
<trans-unit id="TerminalAnsiOptionInvalidArgument">
<source>--ansi expects a single parameter with value 'auto', 'on' (or 'true', 'enable', '1') or 'off' (or 'false', 'disable', '0').</source>
<target state="new">--ansi expects a single parameter with value 'auto', 'on' (or 'true', 'enable', '1') or 'off' (or 'false', 'disable', '0').</target>
<note />
</trans-unit>
<trans-unit id="TerminalNoAnsiOptionDescription">
<source>Disable outputting ANSI escape characters to screen.</source>
<target state="translated">Disabilita l'output dei caratteri di escape ANSI sullo schermo.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,22 @@ Microsoft Testing Platform テレメトリの詳細: https://aka.ms/testingplatf
<target state="translated">テレメトリ プロバイダーは既に設定されています</target>
<note />
</trans-unit>
<trans-unit id="TerminalAnsiOptionDescription">
<source>Control whether ANSI escape characters are emitted.
Valid values are 'auto' (default), 'on' (also accepts 'true', 'enable', '1') or 'off' (also accepts 'false', 'disable', '0').
'on' forces ANSI escape codes (including cursor movement) even when stdout is redirected; pair it with --no-progress if you only want colors.
When both --ansi and --no-ansi are provided, --ansi wins.</source>
<target state="new">Control whether ANSI escape characters are emitted.
Valid values are 'auto' (default), 'on' (also accepts 'true', 'enable', '1') or 'off' (also accepts 'false', 'disable', '0').
'on' forces ANSI escape codes (including cursor movement) even when stdout is redirected; pair it with --no-progress if you only want colors.
When both --ansi and --no-ansi are provided, --ansi wins.</target>
<note />
</trans-unit>
<trans-unit id="TerminalAnsiOptionInvalidArgument">
<source>--ansi expects a single parameter with value 'auto', 'on' (or 'true', 'enable', '1') or 'off' (or 'false', 'disable', '0').</source>
<target state="new">--ansi expects a single parameter with value 'auto', 'on' (or 'true', 'enable', '1') or 'off' (or 'false', 'disable', '0').</target>
<note />
</trans-unit>
<trans-unit id="TerminalNoAnsiOptionDescription">
<source>Disable outputting ANSI escape characters to screen.</source>
<target state="translated">画面への ANSI エスケープ文字の出力を無効にします。</target>
Expand Down
Loading
Loading