mirror of https://github.com/dotnet/tye.git
committed by
GitHub
10 changed files with 263 additions and 10 deletions
@ -0,0 +1,55 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Microsoft.Tye.Hosting.Ansi2Html |
||||
|
{ |
||||
|
public static class Constants |
||||
|
{ |
||||
|
public const string Red = "#800000"; |
||||
|
public const string Black = "#000000"; |
||||
|
public const string Green = "#008000"; |
||||
|
public const string Yellow = "#808000"; |
||||
|
public const string Blue = "#000080"; |
||||
|
public const string Purple = "#800080"; |
||||
|
public const string Cyan = "#008080"; |
||||
|
public const string LightGray = "#c0c0c0"; |
||||
|
public const string DarkGray = "#808080"; |
||||
|
public const string BrightRed = "#ff0000"; |
||||
|
public const string BrightGreen = "#00ff00"; |
||||
|
public const string BrightYellow = "#ffff00"; |
||||
|
public const string BrightBlue = "#0000ff"; |
||||
|
public const string BrightPurple = "#ff00ff"; |
||||
|
public const string BrightCyan = "#00ffff"; |
||||
|
public const string White = "#ffffff"; |
||||
|
|
||||
|
public static Dictionary<string, string> ColorMap = new Dictionary<string, string>() |
||||
|
{ |
||||
|
{ "0", Black }, // Black
|
||||
|
{ "1", Red }, // Red
|
||||
|
{ "2", Green }, // Green
|
||||
|
{ "3", Yellow }, // Yellow
|
||||
|
{ "4", Blue }, // Blue
|
||||
|
{ "5", Purple }, // Purple
|
||||
|
{ "6", Cyan }, // Cyan
|
||||
|
{ "7", LightGray }, // Light Gray
|
||||
|
{ "8", DarkGray }, // Dark Gray
|
||||
|
{ "9", BrightRed }, // Bright Red
|
||||
|
{ "10", BrightGreen }, // Bright Green
|
||||
|
{ "11", BrightYellow }, // Bright Yellow
|
||||
|
{ "12", BrightBlue }, // Bright Blue
|
||||
|
{ "13", BrightPurple }, // Bright Purple
|
||||
|
{ "14", BrightCyan }, // Bright Cyan
|
||||
|
{ "15", White } // White
|
||||
|
}; |
||||
|
|
||||
|
public static class SelectGraphicRenditionParameters |
||||
|
{ |
||||
|
public const int Reset = 0; |
||||
|
|
||||
|
public static HashSet<int> SetForeground = new HashSet<int>() |
||||
|
{ |
||||
|
30, 31, 32, 33, 34, 35, 36, 37,38 |
||||
|
}; |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,49 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Text.RegularExpressions; |
||||
|
|
||||
|
namespace Microsoft.Tye.Hosting.Ansi2Html |
||||
|
{ |
||||
|
public class Converter |
||||
|
{ |
||||
|
private readonly Regex _rule = new Regex("\u001b\\[(?<code>\\d+)(?<args>;\\d+)*m", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.IgnoreCase); |
||||
|
|
||||
|
public string Parse(string input) |
||||
|
{ |
||||
|
var htmlText = _rule.Replace(input, match => |
||||
|
{ |
||||
|
var code = Convert.ToInt32(match.Groups["code"].Value); |
||||
|
var args = match.Groups["args"].Value; |
||||
|
List<string> attributes = args.Split(';').ToList(); |
||||
|
|
||||
|
var tagBuilder = new StringBuilder(); |
||||
|
if (code == Constants.SelectGraphicRenditionParameters.Reset) |
||||
|
{ |
||||
|
tagBuilder.Append("</span>"); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
var color = Constants.White; |
||||
|
if (attributes.Count > 0) |
||||
|
{ |
||||
|
string colorCode = attributes.Last(); |
||||
|
if (Constants.ColorMap.ContainsKey(colorCode)) |
||||
|
{ |
||||
|
color = Constants.ColorMap[colorCode]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
tagBuilder.Append("<span style=\"color:"); |
||||
|
tagBuilder.Append(color); |
||||
|
tagBuilder.Append(";\">"); |
||||
|
} |
||||
|
|
||||
|
return tagBuilder.ToString(); |
||||
|
}); |
||||
|
|
||||
|
return htmlText; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
using Microsoft.Tye.Hosting.Ansi2Html; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Microsoft.Tye.UnitTests; |
||||
|
|
||||
|
public class Ansi2HtmlConverterTests |
||||
|
{ |
||||
|
[Theory] |
||||
|
[InlineData("\u001b[31;1mThis text is red\u001b[0m", $"<span style=\"color:{Constants.Red};\">This text is red</span>")] |
||||
|
[InlineData( |
||||
|
"\u001b[31;1m\u001b[0m\u001b[36;1m\u001b[36;1m 3 | \u001b[0m \u001b[36;1muvicorn\u001b[0m app.main:app --port $Env:PORT --reload --log-level debug\u001b[0m", |
||||
|
$"<span style=\"color:{Constants.Red};\"></span><span style=\"color:{Constants.Red};\"><span style=\"color:{Constants.Red};\"> 3 | </span> <span style=\"color:{Constants.Red};\">uvicorn</span> app.main:app --port $Env:PORT --reload --log-level debug</span>")] |
||||
|
public void ShouldParse(string input, string expected) |
||||
|
{ |
||||
|
Converter converter = new Converter(); |
||||
|
|
||||
|
string actual = converter.Parse(input); |
||||
|
|
||||
|
Assert.Equal(expected, actual); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,52 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Microsoft.Tye.Hosting.Model; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Microsoft.Tye.UnitTests |
||||
|
{ |
||||
|
public class ServiceUnitTests |
||||
|
{ |
||||
|
[Theory] |
||||
|
[MemberData(nameof(ServiceStateTestData))] |
||||
|
public void ServiceStateIsBasedOnReplicaStates(ServiceState expected, List<ReplicaState> replicaStates) |
||||
|
{ |
||||
|
Service service = new(new ServiceDescription("test", null), ServiceSource.Unknown); |
||||
|
|
||||
|
for (int i = 0; i < replicaStates.Count; i++) |
||||
|
{ |
||||
|
string replicaName = i.ToString(); |
||||
|
|
||||
|
service.Replicas.TryAdd(replicaName, new ReplicaStatus(service, replicaName) |
||||
|
{ |
||||
|
State = replicaStates[i], |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
Assert.Equal(expected, service.State); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public static IEnumerable<object[]> ServiceStateTestData => |
||||
|
new List<object[]> |
||||
|
{ |
||||
|
//no replica - should not happen
|
||||
|
new object[] { ServiceState.Unknown, new List<ReplicaState>() }, |
||||
|
|
||||
|
//one replica
|
||||
|
new object[] { ServiceState.Starting, new List<ReplicaState>() { ReplicaState.Added } }, |
||||
|
new object[] { ServiceState.Started, new List<ReplicaState>() { ReplicaState.Started } }, |
||||
|
new object[] { ServiceState.Started, new List<ReplicaState>() { ReplicaState.Ready } }, |
||||
|
new object[] { ServiceState.Started, new List<ReplicaState>() { ReplicaState.Healthy } }, |
||||
|
new object[] { ServiceState.Failed, new List<ReplicaState>() { ReplicaState.Removed } }, |
||||
|
new object[] { ServiceState.Stopped, new List<ReplicaState>() { ReplicaState.Stopped } }, |
||||
|
|
||||
|
//multiple replicas
|
||||
|
new object[] { ServiceState.Starting, new List<ReplicaState>() { ReplicaState.Added, ReplicaState.Started, ReplicaState.Ready, ReplicaState.Healthy } }, |
||||
|
new object[] { ServiceState.Started, new List<ReplicaState>() { ReplicaState.Started, ReplicaState.Ready, ReplicaState.Healthy } }, |
||||
|
new object[] { ServiceState.Degraded, new List<ReplicaState>() { ReplicaState.Removed, ReplicaState.Started, ReplicaState.Ready, ReplicaState.Healthy } }, |
||||
|
new object[] { ServiceState.Degraded, new List<ReplicaState>() { ReplicaState.Stopped, ReplicaState.Started, ReplicaState.Ready, ReplicaState.Healthy } }, |
||||
|
new object[] { ServiceState.Degraded, new List<ReplicaState>() { ReplicaState.Removed, ReplicaState.Stopped, ReplicaState.Started, ReplicaState.Ready, ReplicaState.Healthy } }, |
||||
|
new object[] { ServiceState.Stopped, new List<ReplicaState>() { ReplicaState.Stopped, ReplicaState.Stopped, ReplicaState.Stopped } }, |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue