Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,9 @@ internal void TestCompleted(
string? errorMessage,
Exception? exception,
string? expected,
string? actual)
string? actual,
string? standardOutput,
string? errorOutput)
{
FlatException[] flatExceptions = ExceptionFlattener.Flatten(errorMessage, exception);
TestCompleted(
Expand All @@ -343,7 +345,9 @@ internal void TestCompleted(
informativeMessage,
flatExceptions,
expected,
actual);
actual,
standardOutput,
errorOutput);
}

private void TestCompleted(
Expand All @@ -354,7 +358,9 @@ private void TestCompleted(
string? informativeMessage,
FlatException[] exceptions,
string? expected,
string? actual)
string? actual,
string? standardOutput,
string? errorOutput)
{
if (_testProgressState is null)
{
Expand Down Expand Up @@ -398,7 +404,9 @@ private void TestCompleted(
informativeMessage,
exceptions,
expected,
actual));
actual,
standardOutput,
errorOutput));
}
}

Expand All @@ -416,7 +424,9 @@ private void RenderTestCompleted(
string? informativeMessage,
FlatException[] flatExceptions,
string? expected,
string? actual)
string? actual,
string? standardOutput,
string? errorOutput)
{
if (outcome == TestOutcome.Passed && !GetShowPassedTests())
{
Expand Down Expand Up @@ -458,6 +468,23 @@ private void RenderTestCompleted(
FormatExpectedAndActual(terminal, expected, actual);
FormatStackTrace(terminal, flatExceptions, 0);
FormatInnerExceptions(terminal, flatExceptions);

bool isFailed = outcome is TestOutcome.Fail or TestOutcome.Error or TestOutcome.Timeout or TestOutcome.Canceled;
string? stdoutToShow = _options.ShowStdout switch
{
OutputShowMode.All => standardOutput,
OutputShowMode.Failed => isFailed ? standardOutput : null,
OutputShowMode.None => null,
_ => throw ApplicationStateGuard.Unreachable(),
};
string? stderrToShow = _options.ShowStderr switch
{
OutputShowMode.All => errorOutput,
OutputShowMode.Failed => isFailed ? errorOutput : null,
OutputShowMode.None => null,
_ => throw ApplicationStateGuard.Unreachable(),
};
FormatStandardAndErrorOutput(terminal, stdoutToShow, stderrToShow);
Comment thread
Youssef1313 marked this conversation as resolved.
}

private static void FormatInnerExceptions(ITerminal terminal, FlatException[] exceptions)
Expand Down Expand Up @@ -545,6 +572,36 @@ private static void FormatStackTrace(ITerminal terminal, FlatException[] excepti
terminal.ResetColor();
}

private static void FormatStandardAndErrorOutput(ITerminal terminal, string? standardOutput, string? errorOutput)
{
bool hasStdOut = !RoslynString.IsNullOrWhiteSpace(standardOutput);
bool hasStdErr = !RoslynString.IsNullOrWhiteSpace(errorOutput);
if (!hasStdOut && !hasStdErr)
{
return;
}

terminal.SetColor(TerminalColor.DarkGray);

if (hasStdOut)
{
terminal.Append(SingleIndentation);
terminal.AppendLine(PlatformResources.StandardOutput);
string? standardOutputWithoutSpecialChars = MakeControlCharactersVisible(standardOutput, normalizeWhitespaceCharacters: false);
AppendIndentedLine(terminal, standardOutputWithoutSpecialChars, DoubleIndentation);
}
Comment thread
Youssef1313 marked this conversation as resolved.

if (hasStdErr)
{
terminal.Append(SingleIndentation);
terminal.AppendLine(PlatformResources.StandardError);
string? standardErrorWithoutSpecialChars = MakeControlCharactersVisible(errorOutput, normalizeWhitespaceCharacters: false);
AppendIndentedLine(terminal, standardErrorWithoutSpecialChars, DoubleIndentation);
}
Comment thread
Youssef1313 marked this conversation as resolved.

terminal.ResetColor();
}

private void AppendAssemblyLinkTargetFrameworkAndArchitecture(ITerminal terminal)
{
terminal.AppendLink(_assembly, lineNumber: null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ internal sealed class TerminalTestReporterCommandLineOptionsProvider : ICommandL
public const string OutputOption = "output";
public const string OutputOptionNormalArgument = "normal";
public const string OutputOptionDetailedArgument = "detailed";
public const string ShowStdoutOption = "show-stdout";
public const string ShowStderrOption = "show-stderr";
public const string ShowOutputAllArgument = "all";
public const string ShowOutputFailedArgument = "failed";
public const string ShowOutputNoneArgument = "none";

/// <inheritdoc />
public string Uid => nameof(TerminalTestReporterCommandLineOptionsProvider);
Expand All @@ -38,6 +43,8 @@ public IReadOnlyCollection<CommandLineOption> GetCommandLineOptions()
new(NoProgressOption, PlatformResources.TerminalNoProgressOptionDescription, ArgumentArity.Zero, isHidden: false),
new(NoAnsiOption, PlatformResources.TerminalNoAnsiOptionDescription, ArgumentArity.Zero, 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),
];

public Task<ValidationResult> ValidateOptionArgumentsAsync(CommandLineOption commandOption, string[] arguments)
Expand All @@ -48,9 +55,17 @@ public Task<ValidationResult> ValidateOptionArgumentsAsync(CommandLineOption com
OutputOption => OutputOptionNormalArgument.Equals(arguments[0], StringComparison.OrdinalIgnoreCase) || OutputOptionDetailedArgument.Equals(arguments[0], StringComparison.OrdinalIgnoreCase)
? ValidationResult.ValidTask
: ValidationResult.InvalidTask(PlatformResources.TerminalOutputOptionInvalidArgument),
ShowStdoutOption or ShowStderrOption => IsValidShowOutputArgument(arguments[0])
? ValidationResult.ValidTask
: ValidationResult.InvalidTask(PlatformResources.TerminalShowOutputOptionInvalidArgument),
_ => throw ApplicationStateGuard.Unreachable(),
};

private static bool IsValidShowOutputArgument(string argument)
=> ShowOutputAllArgument.Equals(argument, StringComparison.OrdinalIgnoreCase)
|| ShowOutputFailedArgument.Equals(argument, StringComparison.OrdinalIgnoreCase)
|| ShowOutputNoneArgument.Equals(argument, StringComparison.OrdinalIgnoreCase);

public Task<ValidationResult> ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions)
=> // No problem found
ValidationResult.ValidTask;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,34 @@ internal sealed class TerminalTestReporterOptions
/// Gets a value indicating the ANSI mode.
/// </summary>
public AnsiMode AnsiMode { get; init; }

/// <summary>
/// Gets a value indicating when to show standard output.
/// </summary>
public OutputShowMode ShowStdout { get; init; } = OutputShowMode.All;

/// <summary>
/// Gets a value indicating when to show standard error output.
Comment thread
Evangelink marked this conversation as resolved.
/// </summary>
public OutputShowMode ShowStderr { get; init; } = OutputShowMode.All;
}

internal enum OutputShowMode
{
/// <summary>
/// Always show the output.
/// </summary>
All,

/// <summary>
/// Show the output only for failed tests.
Comment thread
Youssef1313 marked this conversation as resolved.
/// </summary>
Failed,

/// <summary>
/// Never show the output.
/// </summary>
None,
}

internal enum AnsiMode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ await _policiesService.RegisterOnAbortCallbackAsync(
showPassed = () => true;
}

OutputShowMode showStdout = GetShowOutputMode(_commandLineOptions, TerminalTestReporterCommandLineOptionsProvider.ShowStdoutOption);
OutputShowMode showStderr = GetShowOutputMode(_commandLineOptions, TerminalTestReporterCommandLineOptionsProvider.ShowStderrOption);

Func<bool?> shouldShowProgress = noProgress || ansiMode is AnsiMode.NoAnsi or AnsiMode.SimpleAnsi
// User preference is to not show progress.
// Or, we are in terminal that's not capable of changing cursor and we can't update progress in-place.
Expand All @@ -174,9 +177,21 @@ await _policiesService.RegisterOnAbortCallbackAsync(
AnsiMode = ansiMode,
ShowActiveTests = true,
ShowProgress = shouldShowProgress,
ShowStdout = showStdout,
ShowStderr = showStderr,
});
}

private static OutputShowMode GetShowOutputMode(ICommandLineOptions commandLineOptions, string optionName)
=> commandLineOptions.TryGetOptionArgumentList(optionName, out string[]? arguments) && arguments is { Length: > 0 }
? arguments[0] switch
{
string s when TerminalTestReporterCommandLineOptionsProvider.ShowOutputFailedArgument.Equals(s, StringComparison.OrdinalIgnoreCase) => OutputShowMode.Failed,
string s when TerminalTestReporterCommandLineOptionsProvider.ShowOutputNoneArgument.Equals(s, StringComparison.OrdinalIgnoreCase) => OutputShowMode.None,
_ => OutputShowMode.All,
}
: OutputShowMode.All;

private static string GetShortArchitecture(string runtimeIdentifier)
=> runtimeIdentifier.Contains(Dash)
? runtimeIdentifier.Split(Dash, 2)[1]
Expand Down Expand Up @@ -391,6 +406,8 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo
case TestNodeUpdateMessage testNodeStateChanged:

TimeSpan? duration = testNodeStateChanged.TestNode.Properties.SingleOrDefault<TimingProperty>()?.GlobalTiming.Duration;
string? standardOutput = testNodeStateChanged.TestNode.Properties.SingleOrDefault<StandardOutputProperty>()?.StandardOutput;
string? standardError = testNodeStateChanged.TestNode.Properties.SingleOrDefault<StandardErrorProperty>()?.StandardError;
Comment thread
Youssef1313 marked this conversation as resolved.

foreach (FileArtifactProperty artifact in testNodeStateChanged.TestNode.Properties.OfType<FileArtifactProperty>())
{
Expand Down Expand Up @@ -418,7 +435,9 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo
errorState.Explanation,
errorState.Exception,
expected: null,
actual: null);
actual: null,
standardOutput,
standardError);
break;

case FailedTestNodeStateProperty failedState:
Expand All @@ -431,7 +450,9 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo
failedState.Explanation,
failedState.Exception,
expected: failedState.Exception?.Data["assert.expected"] as string,
actual: failedState.Exception?.Data["assert.actual"] as string);
actual: failedState.Exception?.Data["assert.actual"] as string,
standardOutput,
standardError);
break;

case TimeoutTestNodeStateProperty timeoutState:
Expand All @@ -444,7 +465,9 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo
timeoutState.Explanation,
timeoutState.Exception,
expected: null,
actual: null);
actual: null,
standardOutput,
standardError);
break;

#pragma warning disable CS0618 // Type or member is obsolete
Expand All @@ -459,7 +482,9 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo
cancelledState.Explanation,
cancelledState.Exception,
expected: null,
actual: null);
actual: null,
standardOutput,
standardError);
break;

case PassedTestNodeStateProperty:
Expand All @@ -472,7 +497,9 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo
errorMessage: null,
exception: null,
expected: null,
actual: null);
actual: null,
standardOutput,
standardError);
break;

case SkippedTestNodeStateProperty skippedState:
Expand All @@ -485,7 +512,9 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo
errorMessage: null,
exception: null,
expected: null,
actual: null);
actual: null,
standardOutput,
standardError);
break;

case DiscoveredTestNodeStateProperty:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,17 @@ Valid values are 'Normal', 'Detailed'. Default is 'Normal'.</value>
<data name="TerminalOutputOptionInvalidArgument" xml:space="preserve">
<value>--output expects a single parameter with value 'Normal' or 'Detailed'.</value>
</data>
<data name="TerminalShowStdoutOptionDescription" xml:space="preserve">
<value>Determines when to show captured standard output of a test.
Valid values are 'All', 'Failed', 'None'. Default is 'All'.</value>
</data>
<data name="TerminalShowStderrOptionDescription" xml:space="preserve">
<value>Determines when to show captured error output of a test.
Valid values are 'All', 'Failed', 'None'. Default is 'All'.</value>
</data>
<data name="TerminalShowOutputOptionInvalidArgument" xml:space="preserve">
<value>--show-stdout and --show-stderr expect a single parameter with value 'All', 'Failed', or 'None'.</value>
</data>
<data name="PlatformCommandLineTimeoutArgumentErrorMessage" xml:space="preserve">
<value>'timeout' option should have one argument as string in the format &lt;value&gt;[h|m|s] where 'value' is float</value>
</data>
Expand All @@ -625,6 +636,12 @@ Takes one argument as string in the format &lt;value&gt;[h|m|s] where 'value' is
<data name="MissingClientPortFoJsonRpc" xml:space="preserve">
<value>Expected --client-port when jsonRpc protocol is used.</value>
</data>
<data name="StandardError" xml:space="preserve">
<value>Error output</value>
</data>
<data name="StandardOutput" xml:space="preserve">
<value>Standard output</value>
</data>
<data name="DiscoveringTestsFrom" xml:space="preserve">
<value>Discovering tests from</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,16 @@ Může mít jenom jeden argument jako řetězec ve formátu &lt;value&gt;[h|m|s]
<target state="translated">Trasování zásobníku</target>
<note />
</trans-unit>
<trans-unit id="StandardError">
<source>Error output</source>
<target state="translated">Chybový výstup</target>
<note />
</trans-unit>
<trans-unit id="StandardOutput">
<source>Standard output</source>
<target state="translated">Standardní výstup</target>
<note />
</trans-unit>
<trans-unit id="StartingTestSession">
<source>Starting test session.</source>
<target state="translated">Spouští se testovací relace.</target>
Expand Down Expand Up @@ -810,6 +820,25 @@ Platné hodnoty jsou Normal a Detailed. Výchozí hodnota je Normal.</target>
<target state="translated">--output očekává jeden parametr s hodnotou Normal nebo Detailed.</target>
<note />
</trans-unit>
<trans-unit id="TerminalShowOutputOptionInvalidArgument">
<source>--show-stdout and --show-stderr expect a single parameter with value 'All', 'Failed', or 'None'.</source>
<target state="new">--show-stdout and --show-stderr expect a single parameter with value 'All', 'Failed', or 'None'.</target>
<note />
</trans-unit>
<trans-unit id="TerminalShowStderrOptionDescription">
<source>Determines when to show captured error output of a test.
Valid values are 'All', 'Failed', 'None'. Default is 'All'.</source>
<target state="new">Determines when to show captured error output of a test.
Valid values are 'All', 'Failed', 'None'. Default is 'All'.</target>
<note />
</trans-unit>
<trans-unit id="TerminalShowStdoutOptionDescription">
<source>Determines when to show captured standard output of a test.
Valid values are 'All', 'Failed', 'None'. Default is 'All'.</source>
<target state="new">Determines when to show captured standard output of a test.
Valid values are 'All', 'Failed', 'None'. Default is 'All'.</target>
<note />
</trans-unit>
Comment thread
Youssef1313 marked this conversation as resolved.
<trans-unit id="TerminalTestReporterDescription">
<source>Writes test results to terminal.</source>
<target state="translated">Zapíše výsledky testu do terminálu.</target>
Expand Down
Loading
Loading