Skip to content

Commit 427918c

Browse files
authored
Show ansi colors in azdo and gh actions CI (#49014)
1 parent a3475f2 commit 427918c

File tree

7 files changed

+354
-232
lines changed

7 files changed

+354
-232
lines changed

src/Cli/dotnet/Commands/Test/Terminal/AnsiTerminalTestProgressFrame.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public void AppendTestWorkerProgress(TestProgressState progress, RenderedProgres
3434

3535
terminal.Append('[');
3636
charsTaken++;
37-
terminal.SetColor(TerminalColor.Green);
37+
terminal.SetColor(TerminalColor.DarkGreen);
3838
terminal.Append('✓');
3939
charsTaken++;
4040
string passedText = passed.ToString(CultureInfo.CurrentCulture);
@@ -45,7 +45,7 @@ public void AppendTestWorkerProgress(TestProgressState progress, RenderedProgres
4545
terminal.Append('/');
4646
charsTaken++;
4747

48-
terminal.SetColor(TerminalColor.Red);
48+
terminal.SetColor(TerminalColor.DarkRed);
4949
terminal.Append('x');
5050
charsTaken++;
5151
string failedText = failed.ToString(CultureInfo.CurrentCulture);
@@ -56,7 +56,7 @@ public void AppendTestWorkerProgress(TestProgressState progress, RenderedProgres
5656
terminal.Append('/');
5757
charsTaken++;
5858

59-
terminal.SetColor(TerminalColor.Yellow);
59+
terminal.SetColor(TerminalColor.DarkYellow);
6060
terminal.Append('↓');
6161
charsTaken++;
6262
string skippedText = skipped.ToString(CultureInfo.CurrentCulture);
Lines changed: 21 additions & 211 deletions
Original file line numberDiff line numberDiff line change
@@ -1,135 +1,52 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4-
using System.Globalization;
4+
using Microsoft.DotNet.Cli.Commands.Test.Terminal;
55

6-
namespace Microsoft.DotNet.Cli.Commands.Test.Terminal;
6+
namespace Microsoft.Testing.Platform.OutputDevice.Terminal;
77

88
/// <summary>
99
/// Non-ANSI terminal that writes text using the standard Console.Foreground color capabilities to stay compatible with
1010
/// standard Windows command line, and other command lines that are not capable of ANSI, or when output is redirected.
1111
/// </summary>
12-
internal sealed class NonAnsiTerminal : ITerminal
12+
internal sealed class NonAnsiTerminal : SimpleTerminal
1313
{
14-
private readonly IConsole _console;
1514
private readonly ConsoleColor _defaultForegroundColor;
16-
private readonly StringBuilder _stringBuilder = new();
17-
private bool _isBatching;
15+
private bool? _colorNotSupported;
1816

1917
public NonAnsiTerminal(IConsole console)
20-
{
21-
_console = console;
22-
_defaultForegroundColor = _console.GetForegroundColor();
23-
}
24-
25-
public int Width => _console.IsOutputRedirected ? int.MaxValue : _console.BufferWidth;
18+
: base(console)
19+
=> _defaultForegroundColor = IsForegroundColorNotSupported() ? ConsoleColor.Black : console.GetForegroundColor();
2620

27-
public int Height => _console.IsOutputRedirected ? int.MaxValue : _console.BufferHeight;
28-
29-
public void Append(char value)
21+
public override void SetColor(TerminalColor color)
3022
{
31-
if (_isBatching)
32-
{
33-
_stringBuilder.Append(value);
34-
}
35-
else
23+
if (IsForegroundColorNotSupported())
3624
{
37-
_console.Write(value);
25+
return;
3826
}
39-
}
4027

41-
public void Append(string value)
42-
{
43-
if (_isBatching)
44-
{
45-
_stringBuilder.Append(value);
46-
}
47-
else
48-
{
49-
_console.Write(value);
50-
}
51-
}
52-
53-
public void AppendLine()
54-
{
55-
if (_isBatching)
56-
{
57-
_stringBuilder.AppendLine();
58-
}
59-
else
60-
{
61-
_console.WriteLine();
62-
}
28+
Console.SetForegroundColor(ToConsoleColor(color));
6329
}
6430

65-
public void AppendLine(string value)
31+
public override void ResetColor()
6632
{
67-
if (_isBatching)
68-
{
69-
_stringBuilder.AppendLine(value);
70-
}
71-
else
33+
if (IsForegroundColorNotSupported())
7234
{
73-
_console.WriteLine(value);
35+
return;
7436
}
75-
}
76-
77-
public void AppendLink(string path, int? lineNumber)
78-
{
79-
Append(path);
80-
if (lineNumber.HasValue)
81-
{
82-
Append($":{lineNumber}");
83-
}
84-
}
85-
86-
public void SetColor(TerminalColor color)
87-
{
88-
if (_isBatching)
89-
{
90-
_console.Write(_stringBuilder.ToString());
91-
_stringBuilder.Clear();
92-
}
93-
94-
_console.SetForegroundColor(ToConsoleColor(color));
95-
}
96-
97-
public void ResetColor()
98-
{
99-
if (_isBatching)
100-
{
101-
_console.Write(_stringBuilder.ToString());
102-
_stringBuilder.Clear();
103-
}
104-
105-
_console.SetForegroundColor(_defaultForegroundColor);
106-
}
107-
108-
public void ShowCursor()
109-
{
110-
// nop
111-
}
11237

113-
public void HideCursor()
114-
{
115-
// nop
38+
Console.SetForegroundColor(_defaultForegroundColor);
11639
}
11740

118-
public void StartUpdate()
41+
private bool IsForegroundColorNotSupported()
11942
{
120-
if (_isBatching)
121-
{
122-
throw new InvalidOperationException(CliCommandStrings.ConsoleIsAlreadyInBatchingMode);
123-
}
43+
_colorNotSupported ??= RuntimeInformation.IsOSPlatform(OSPlatform.Create("ANDROID")) ||
44+
RuntimeInformation.IsOSPlatform(OSPlatform.Create("IOS")) ||
45+
RuntimeInformation.IsOSPlatform(OSPlatform.Create("TVOS")) ||
46+
RuntimeInformation.IsOSPlatform(OSPlatform.Create("WASI")) ||
47+
RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"));
12448

125-
_stringBuilder.Clear();
126-
_isBatching = true;
127-
}
128-
129-
public void StopUpdate()
130-
{
131-
_console.Write(_stringBuilder.ToString());
132-
_isBatching = false;
49+
return _colorNotSupported.Value;
13350
}
13451

13552
private ConsoleColor ToConsoleColor(TerminalColor color) => color switch
@@ -153,111 +70,4 @@ public void StopUpdate()
15370
TerminalColor.White => ConsoleColor.White,
15471
_ => _defaultForegroundColor,
15572
};
156-
157-
public void EraseProgress()
158-
{
159-
// nop
160-
}
161-
162-
public void RenderProgress(TestProgressState?[] progress)
163-
{
164-
int count = 0;
165-
foreach (TestProgressState? p in progress)
166-
{
167-
if (p == null)
168-
{
169-
continue;
170-
}
171-
172-
count++;
173-
174-
string durationString = HumanReadableDurationFormatter.Render(p.Stopwatch.Elapsed);
175-
176-
int passed = p.PassedTests;
177-
int failed = p.FailedTests;
178-
int skipped = p.SkippedTests;
179-
int retried = p.RetriedFailedTests;
180-
181-
// Use just ascii here, so we don't put too many restrictions on fonts needing to
182-
// properly show unicode, or logs being saved in particular encoding.
183-
Append('[');
184-
SetColor(TerminalColor.DarkGreen);
185-
Append('+');
186-
Append(passed.ToString(CultureInfo.CurrentCulture));
187-
ResetColor();
188-
189-
Append('/');
190-
191-
SetColor(TerminalColor.DarkRed);
192-
Append('x');
193-
Append(failed.ToString(CultureInfo.CurrentCulture));
194-
ResetColor();
195-
196-
Append('/');
197-
198-
SetColor(TerminalColor.DarkYellow);
199-
Append('?');
200-
Append(skipped.ToString(CultureInfo.CurrentCulture));
201-
ResetColor();
202-
203-
if (retried > 0)
204-
{
205-
Append('/');
206-
SetColor(TerminalColor.Gray);
207-
Append('r');
208-
Append(retried.ToString(CultureInfo.CurrentCulture));
209-
ResetColor();
210-
}
211-
212-
Append(']');
213-
214-
Append(' ');
215-
Append(p.AssemblyName);
216-
217-
if (p.TargetFramework != null || p.Architecture != null)
218-
{
219-
Append(" (");
220-
if (p.TargetFramework != null)
221-
{
222-
Append(p.TargetFramework);
223-
Append('|');
224-
}
225-
226-
if (p.Architecture != null)
227-
{
228-
Append(p.Architecture);
229-
}
230-
231-
Append(')');
232-
}
233-
234-
TestDetailState? activeTest = p.TestNodeResultsState?.GetRunningTasks(1).FirstOrDefault();
235-
if (!string.IsNullOrWhiteSpace(activeTest?.Text))
236-
{
237-
Append(" - ");
238-
Append(activeTest.Text);
239-
Append(' ');
240-
}
241-
242-
Append(durationString);
243-
244-
AppendLine();
245-
}
246-
247-
// Do not render empty lines when there is nothing to show.
248-
if (count > 0)
249-
{
250-
AppendLine();
251-
}
252-
}
253-
254-
public void StartBusyIndicator()
255-
{
256-
// nop
257-
}
258-
259-
public void StopBusyIndicator()
260-
{
261-
// nop
262-
}
26373
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using Microsoft.DotNet.Cli.Commands.Test.Terminal;
5+
6+
namespace Microsoft.Testing.Platform.OutputDevice.Terminal;
7+
8+
9+
/// <summary>
10+
/// Simple terminal that uses 4-bit ANSI for colors but does not move cursor and does not do other fancy stuff to stay compatible with CI systems like AzDO.
11+
/// The colors are set on start of every line to properly color multiline strings in AzDO output.
12+
/// </summary>
13+
internal sealed class SimpleAnsiTerminal : SimpleTerminal
14+
{
15+
private string? _foregroundColor;
16+
private bool _prependColor;
17+
18+
public SimpleAnsiTerminal(IConsole console)
19+
: base(console)
20+
{
21+
}
22+
23+
public override void Append(string value)
24+
{
25+
// Previous write appended line, so we need to prepend color.
26+
if (_prependColor)
27+
{
28+
Console.Write(_foregroundColor);
29+
// This line is not adding new line at the end, so we don't need to prepend color on next line.
30+
_prependColor = false;
31+
}
32+
33+
Console.Write(SetColorPerLine(value));
34+
}
35+
36+
public override void AppendLine(string value)
37+
{
38+
// Previous write appended line, so we need to prepend color.
39+
if (_prependColor)
40+
{
41+
Console.Write(_foregroundColor);
42+
}
43+
44+
Console.WriteLine(SetColorPerLine(value));
45+
// This call appended new line so the next write to console needs to prepend color.
46+
_prependColor = true;
47+
}
48+
49+
public override void SetColor(TerminalColor color)
50+
{
51+
string setColor = $"{AnsiCodes.CSI}{(int)color}{AnsiCodes.SetColor}";
52+
_foregroundColor = setColor;
53+
Console.Write(setColor);
54+
// This call set the color for current line, no need to prepend on next write.
55+
_prependColor = false;
56+
}
57+
58+
public override void ResetColor()
59+
{
60+
_foregroundColor = null;
61+
_prependColor = false;
62+
Console.Write(AnsiCodes.SetDefaultColor);
63+
}
64+
65+
private string? SetColorPerLine(string value)
66+
=> _foregroundColor == null ? value : value.Replace("\n", $"\n{_foregroundColor}");
67+
}

0 commit comments

Comments
 (0)